Skip to content

Commit

Permalink
Add a validating syntax parser.
Browse files Browse the repository at this point in the history
Closes square#46. Fixes square#47.

Prior to this commit the gem failed to validate the RRULE's syntax:

* If a keyword was misspelled, it would *silently* be ignored
* If a keyword appeared multiple times only the last occurance was used
* If the mutually exclusive keywords COUNT and UNTIL were used together
  then no exception was raised

```ruby
> rrule = RRule.parse('FREQ=DAILY;COUNT=3;COUNT=7;UNTIL=20200302;INTERVALLLLL=9')
 => #<RRule::Rule:0x000055db33885018 ...
> rrule.instance_variable_get(:@options)
 => {:interval=>1, :wkst=>1, :freq=>"DAILY", :count=>7, :until=>2020-03-02 00:00:00 +0100, ...
```

Therefore we add a validating parser with comprehensive error messages:

```ruby
> rrule = RRule.parse('FREQ=DAILY;COUNT=3;COUNT=7;UNTIL=20200302;INTERVALLLLL=9')
Traceback (most recent call last):
...
RRule::InvalidRRule (SyntaxError)

* unknown keyword 'INTERVALLLLL' at position 42:

        'FREQ=DAILY;COUNT=3;COUNT=7;UNTIL=20200302;INTERVALLLLL=9'
                                                   ^^^^^^^^^^^^
* keyword 'COUNT' appeared more than once at positions 11 and 19:

        'FREQ=DAILY;COUNT=3;COUNT=7;UNTIL=20200302;INTERVALLLLL=9'
                    ^^^^^   ^^^^^
* keywords 'COUNT' and 'UNTIL' are mutually exclusive at positions 11, 19 and 27:

        'FREQ=DAILY;COUNT=3;COUNT=7;UNTIL=20200302;INTERVALLLLL=9'
                    ^^^^^   ^^^^^   ^^^^^
```
  • Loading branch information
leoarnold committed Jul 4, 2022
1 parent cafd904 commit 757b7ab
Show file tree
Hide file tree
Showing 9 changed files with 1,478 additions and 50 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Description

rrule is a minimalist library for expanding RRULEs, with a goal of being fully compliant with [iCalendar spec](https://tools.ietf.org/html/rfc2445).
rrule is a minimalist library for expanding RRULEs, with a goal of being fully compliant with [iCalendar spec](https://tools.ietf.org/html/rfc5545).

## Examples

Expand Down Expand Up @@ -62,6 +62,34 @@ rrule.all
=> [2016-07-01 00:00:00 -0700, 2016-07-03 00:00:00 -0700]
```

### ActiveModel validator

In every project using `ActiveModel` the `RruleValidator` will automatically be loaded and can be used like this

```ruby
class MyEvent
include ActiveModel::Validations

attr_reader :recurrence_rule

def initialize(recurrence_rule)
@recurrence_rule = recurrence_rule
end

validates :recurrence_rule, rrule: true
end
```

or in a Rails app

```ruby
class MyEvent < ApplicationRecord
field :recurrence_rule, type: String

validates :recurrence_rule, rrule: true
end
```

## License

Copyright 2018 Square Inc.
Expand Down
38 changes: 38 additions & 0 deletions lib/rrule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'active_support/all'

module RRule
autoload :Parser, 'rrule/parser'
autoload :Rule, 'rrule/rule'
autoload :Context, 'rrule/context'
autoload :Weekday, 'rrule/weekday'
Expand All @@ -24,11 +25,48 @@ module RRule
autoload :AllOccurrences, 'rrule/generators/all_occurrences'
autoload :BySetPosition, 'rrule/generators/by_set_position'

KEYWORDS = %w[
FREQ
UNTIL
COUNT
INTERVAL
BYSECOND
BYMINUTE
BYHOUR
BYDAY
BYMONTHDAY
BYYEARDAY
BYWEEKNO
BYMONTH
BYSETPOS
WKST
].freeze

FREQUENCIES = %w[
SECONDLY
MINUTELY
HOURLY
DAILY
WEEKLY
MONTHLY
YEARLY
].freeze

WEEKDAYS = %w[SU MO TU WE TH FR SA].freeze

def self.parse(rrule, **options)
Rule.new(rrule, **options)
end

def self.valid?(rrule)
Parser.parse!(rrule)

true
rescue InvalidRRule
false
end

class InvalidRRule < StandardError; end
end

autoload(:RruleValidator, 'rrule_validator')
2 changes: 1 addition & 1 deletion lib/rrule/frequencies/frequency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def self.for_options(options)
when 'YEARLY'
Yearly
else
raise InvalidRRule, 'Valid FREQ value is required'
raise NotImplementedError, "Frequency '#{options[:freq]}' not implemented"
end
end

Expand Down
Loading

0 comments on commit 757b7ab

Please sign in to comment.