# Hiccup

[![Gem Version](https://badge.fury.io/rb/hiccup.svg)](https://rubygems.org/gems/hiccup)
[![Code Climate](https://codeclimate.com/github/boblail/hiccup.svg)](https://codeclimate.com/github/boblail/hiccup)
[![Build Status](https://travis-ci.org/boblail/hiccup.svg)](https://travis-ci.org/boblail/hiccup)

Hiccup mixes a-la-cart recurrence features into your recurring model. It doesn't dictate the data structure of your model, just the interface. It works like Devise does for authenticatable models.

Hiccup does provide a lightweight `Schedule` class that mixes in all of Hiccup's feature, but you don't have to use Hiccup's Schedule if you don't want to.

### Usage

```ruby
class Schedule
  extend Hiccup
  
  hiccup :enumerable,
         :validatable,
         :humanizable,
         :inferable,
         :serializable => [:ical]
  
end
```

### Interface

Hiccup requires that a recurring object expose the following properties

  - **kind**: one of `:never`, `:weekly`, `:monthly`, `:annually`
  - **start_date**: the date when the recurrence should start
  - **ends**: either `true` or `false`, indicating whether the recurrence has an end date
  - **end_date**: the date when the recurrence should end
  - **skip**: the number of instances to skip (You'd set `skip` to 2 to specify an event that occurs every _other_ week, for example)
  - **weekly_pattern**: an array of weekdays on which a weekly event should recur
  - **monthly_pattern**: an array of recurrence rules for a monthly event

### Examples


```ruby
# Every other Monday
Schedule.new(:kind => :weekly, :weekly_pattern => ["Monday"])

# Every year on June 21 (starting in 1999)
Schedule.new(:kind => :yearly, :start_date => Date.new(1999, 6, 21))

# The second and fourth Sundays of the month
Schedule.new(:kind => :monthly, :monthly_pattern => [[2, "Sunday"], [4, "Sunday"]])
```


# Modules

### Enumerable

Supplies methods for creating instances of a recurring pattern

Examples

```ruby
schedule = Schedule.new(
  :kind => :weekly,
  :weekly_pattern => %w{Monday Wednesday Friday},
  :start_date => Date.new(2009, 3, 15))

# includes?
schedule.includes? Date.new(2009, 5, 20) # => true
schedule.includes? Date.new(2009, 3, 15) # => false (3/15/09 is a Sunday)

# occurrences_between
schedule.occurrences_between(
  Date.new(2009, 3, 26),
  Date.new(2009, 3, 31)) #=> [Fri, 27 Mar 2009, Mon, 30 Mar 2009]

# n_occurrences_before
schedule.n_occurrences_before(3, Date.new(2009, 3, 31)) # => [Mon, 30 Mar 2009, Fri, 27 Mar 2009, Wed, 25 Mar 2009]
```



### Validatable

Mixes in ActiveModel validations for recurrence models



### Humanizable

Represents a recurring pattern in a human-readable string

Examples:

```ruby
# A monthly recurrence
schedule = Schedule.new(:kind => :monthly, :monthly_pattern => [[-1, "Tuesday"]])
schedule.humanize # => "The last Tuesday of every month"

# With skips
schedule = Schedule.new(:kind => :weekly, :weekly_pattern => %w[Sunday], :skip => 2)
schedule.humanize # => "Every other Sunday"

# An anniversary
schedule = Schedule.new(:kind => :annually, start_date: Date.new(2012, 10, 1))
schedule.humanize # => "Every year on October 1"
```



### Inferable

Infers a schedule from an array of dates

Examples:

```ruby
schedule = Schedule.infer %w{2012-7-9 2012-8-13 2012-9-10}
schedule.humanize # => "The second Monday of every month"

schedule = Schedule.infer %w{2010-3-4 2011-3-4 2012-3-4}
schedule.humanize # => "Every year on March 4"

schedule = Schedule.infer %w{2012-3-6 2012-3-8 2012-3-15 2012-3-20 2012-3-27 2012-3-29}
schedule.humanize # => "Every Tuesday and Thursday"
```



### Serializable

Supports serializing and deserializing a recurrence pattern to an array of formats

Examples:

```ruby
schedule = Schedule.from_ical <<-ICAL
DTSTART;VALUE=DATE-TIME:20090101T000000Z
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH
ICAL
schedule.humanize # => "Tuesday and Thursday of every other week"

schedule = Schedule.new(
  :kind => :weekly,
  :weekly_pattern => %w{Tuesday Thursday},
  :start_date => DateTime.new(2009, 1, 1),
  :skip => 2)
schedule.to_ical # => "DTSTART;VALUE=DATE-TIME:20090101T000000Z\nRRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=TU,TH\n"
```



## License

Copyright (c) 2012 Bob Lail, released under the MIT license