ice_cube_model 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cefa6dfe8e2c38fdf4d6440fc779013c3b983133
4
+ data.tar.gz: e0ffa905699ef4e8de88e4859a106874864b76de
5
+ SHA512:
6
+ metadata.gz: 24235214a4c1a1296c4b39f4fd331e1e214fc2ac312aab69fb9b6153977a555bf8022706729406cbff0fa35fc8de8e667f6022cca82c1c26b045669e3a0c466e
7
+ data.tar.gz: a4b9fad410e469dcb850ec87953444e956b3db1f11b3335742fadd824fb6616bddd6c2b9ce816256875b5b4d4d3b98993b70c0a311f4c57e280e19c3a5b6b851
data/.gitignore ADDED
@@ -0,0 +1,36 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+ .rspec
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ Gemfile.lock
32
+ .ruby-version
33
+ .ruby-gemset
34
+
35
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
+ .rvmrc
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+
4
+ Style/HashSyntax:
5
+ EnforcedStyle: hash_rockets
6
+
7
+ Style/AccessModifierIndentation:
8
+ EnforcedStyle: outdent
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Metrics/LineLength:
14
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,119 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ice_cube_model (0.0.1)
5
+ activesupport (>= 3.0.0)
6
+ ice_cube (= 0.13.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.2.4)
12
+ i18n (~> 0.7)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.3, >= 0.3.4)
16
+ tzinfo (~> 1.1)
17
+ ast (2.1.0)
18
+ astrolabe (1.3.1)
19
+ parser (~> 2.2)
20
+ coderay (1.1.0)
21
+ diff-lcs (1.2.5)
22
+ docile (1.1.5)
23
+ ffi (1.9.10)
24
+ formatador (0.2.5)
25
+ guard (2.13.0)
26
+ formatador (>= 0.2.4)
27
+ listen (>= 2.7, <= 4.0)
28
+ lumberjack (~> 1.0)
29
+ nenv (~> 0.1)
30
+ notiffany (~> 0.0)
31
+ pry (>= 0.9.12)
32
+ shellany (~> 0.0)
33
+ thor (>= 0.18.1)
34
+ guard-compat (1.2.1)
35
+ guard-rspec (4.6.4)
36
+ guard (~> 2.1)
37
+ guard-compat (~> 1.1)
38
+ rspec (>= 2.99.0, < 4.0)
39
+ guard-rubocop (1.2.0)
40
+ guard (~> 2.0)
41
+ rubocop (~> 0.20)
42
+ i18n (0.7.0)
43
+ ice_cube (0.13.0)
44
+ json (1.8.3)
45
+ listen (3.0.3)
46
+ rb-fsevent (>= 0.9.3)
47
+ rb-inotify (>= 0.9)
48
+ lumberjack (1.0.9)
49
+ method_source (0.8.2)
50
+ minitest (5.8.1)
51
+ nenv (0.2.0)
52
+ notiffany (0.0.8)
53
+ nenv (~> 0.1)
54
+ shellany (~> 0.0)
55
+ parser (2.2.3.0)
56
+ ast (>= 1.1, < 3.0)
57
+ powerpack (0.1.1)
58
+ pry (0.10.2)
59
+ coderay (~> 1.1.0)
60
+ method_source (~> 0.8.1)
61
+ slop (~> 3.4)
62
+ rainbow (2.0.0)
63
+ rake (10.4.2)
64
+ rb-fsevent (0.9.6)
65
+ rb-inotify (0.9.5)
66
+ ffi (>= 0.5.0)
67
+ rspec (3.3.0)
68
+ rspec-core (~> 3.3.0)
69
+ rspec-expectations (~> 3.3.0)
70
+ rspec-mocks (~> 3.3.0)
71
+ rspec-core (3.3.2)
72
+ rspec-support (~> 3.3.0)
73
+ rspec-expectations (3.3.1)
74
+ diff-lcs (>= 1.2.0, < 2.0)
75
+ rspec-support (~> 3.3.0)
76
+ rspec-mocks (3.3.2)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.3.0)
79
+ rspec-support (3.3.0)
80
+ rubocop (0.34.2)
81
+ astrolabe (~> 1.3)
82
+ parser (>= 2.2.2.5, < 3.0)
83
+ powerpack (~> 0.1)
84
+ rainbow (>= 1.99.1, < 3.0)
85
+ ruby-progressbar (~> 1.4)
86
+ ruby-progressbar (1.7.5)
87
+ shellany (0.0.1)
88
+ simplecov (0.10.0)
89
+ docile (~> 1.1.0)
90
+ json (~> 1.8)
91
+ simplecov-html (~> 0.10.0)
92
+ simplecov-html (0.10.0)
93
+ slop (3.6.0)
94
+ spring (1.4.0)
95
+ spring-commands-rspec (1.0.4)
96
+ spring (>= 0.9.1)
97
+ thor (0.19.1)
98
+ thread_safe (0.3.5)
99
+ tzinfo (1.2.2)
100
+ thread_safe (~> 0.1)
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ guard
107
+ guard-rspec
108
+ guard-rubocop
109
+ ice_cube_model!
110
+ pry
111
+ rake
112
+ rspec
113
+ rubocop
114
+ simplecov
115
+ spring
116
+ spring-commands-rspec
117
+
118
+ BUNDLED WITH
119
+ 1.10.6
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Matt Nichols
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # ice_cube_model
2
+ Extend any object with [ice_cube](https://github.com/seejohnrun/ice_cube) (calendar repeating event) capabilities.
3
+
4
+ Add ice_cube methods to classes (e.g. active_record, active_model).
5
+
6
+ ## installation
7
+
8
+ gem install ice_cube_model
9
+
10
+ or add to Gemfile
11
+
12
+ gem "ice_cube_model"
13
+
14
+
15
+ ## example
16
+
17
+ ```ruby
18
+ class Appointment
19
+ attr_accessor
20
+ :repeat_start_date,
21
+ :repeat_interval,
22
+ :repeat_year,
23
+ :repeat_month,
24
+ :repeat_day,
25
+ :repeat_weekday
26
+
27
+ include ::IceCubeModel::Base
28
+ end
29
+
30
+ appointment = Appointment.new
31
+ appointment.repeat_start_date = ::Date.new(2015, 3, 5)
32
+ appointment.repeat_day = 5
33
+
34
+ appointment.occurrences_between(::Date.new(2015, 3, 5), ::Date.new(2015, 6, 5))
35
+ # => [2015-03-05, 2015-04-05, 2015-06-05]
36
+ ```
37
+
38
+ ## remap attributes
39
+
40
+ If needed, you can remap the attributes expected by ice_cube to other attributes or methods on the class.
41
+
42
+ ```ruby
43
+
44
+ # map to another attribute
45
+ class Appointment
46
+ attr_accessor
47
+ :start_date,
48
+ :repeat_interval,
49
+ :repeat_year,
50
+ :repeat_month,
51
+ :repeat_day,
52
+ :repeat_weekday
53
+
54
+ include ::IceCubeModel::Base
55
+
56
+ with_repeat_param :repeat_start_date, :start_date
57
+ end
58
+
59
+ # map to a method
60
+ class Appointment
61
+ attr_accessor
62
+ :repeat_interval,
63
+ :repeat_year,
64
+ :repeat_month,
65
+ :repeat_day,
66
+ :repeat_weekday
67
+
68
+ include ::IceCubeModel::Base
69
+
70
+ with_repeat_param :repeat_start_date, :calculate_start_date
71
+
72
+ def calculate_start_date
73
+ Date.current + 7.days
74
+ end
75
+ end
76
+
77
+ # map to a lambda
78
+ class Appointment
79
+ attr_accessor
80
+ :repeat_interval,
81
+ :repeat_year,
82
+ :repeat_month,
83
+ :repeat_day,
84
+ :repeat_weekday
85
+
86
+ include ::IceCubeModel::Base
87
+
88
+ with_repeat_param :repeat_start_date, lambda { Date.current + 7.days }
89
+ end
90
+ ```
91
+
92
+ ## notes
93
+ - This gem is a work-in-progress.
94
+ - `occurrences_between` is the only method currently supported.
95
+ - Does not yet support all recurrence options. More coming.
96
+
97
+ ## todo
98
+ - Add more examples to specs and README
99
+ - Add ability to do Nth types of repeats (e.g. last friday of the month, first and third tuesday of every month). These are supported by ice_cube.
100
+
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'ice_cube_model/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ice_cube_model'
7
+ s.version = IceCubeModel::VERSION
8
+ s.authors = ['Matt Nichols']
9
+ s.email = ['matt@nichols.name']
10
+ s.homepage = 'https://github.com/mattnichols/ice_cube_model'
11
+ s.summary = 'Extend any class with ice_cube (calendar repeating events) capabilities.'
12
+ s.description = 'Add ice_cube methods to classes (e.g. active_record, active_model).'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_paths = ['lib']
17
+
18
+ ##
19
+ # Dependencies
20
+ #
21
+ s.add_dependency 'ice_cube', '= 0.13.0'
22
+ s.add_dependency 'activesupport', '>= 3.0.0'
23
+
24
+ ##
25
+ # Development Dependencies
26
+ #
27
+ s.add_development_dependency 'rake'
28
+ s.add_development_dependency 'pry'
29
+ s.add_development_dependency 'rspec'
30
+ s.add_development_dependency 'simplecov'
31
+ s.add_development_dependency 'rubocop'
32
+ s.add_development_dependency 'guard'
33
+ s.add_development_dependency 'guard-rspec'
34
+ s.add_development_dependency 'guard-rubocop'
35
+ s.add_development_dependency 'spring'
36
+ s.add_development_dependency 'spring-commands-rspec'
37
+ end
@@ -0,0 +1,60 @@
1
+ module IceCubeModel
2
+ module Base
3
+ def self.included(base)
4
+ base.extend(::IceCubeModel::Base::ClassMethods)
5
+ end
6
+
7
+ def events_between(start, through)
8
+ params = read_cyclical_params
9
+ schedule = ::IceCubeModel::Util.build_schedule(params)
10
+ schedule.occurrences_between(::IceCubeModel::Util.sanitize_date_param(start), ::IceCubeModel::Util.sanitize_date_param(through)).map(&:to_date)
11
+ end
12
+
13
+ private
14
+
15
+ def read_repeat_parameter(param_name)
16
+ send(self.class.repeat_parameter_mappings[param_name])
17
+ end
18
+
19
+ def read_cyclical_params
20
+ {
21
+ :repeat_start_date => ::IceCubeModel::Util.sanitize_date_param(read_repeat_parameter(:repeat_start_date)),
22
+ :repeat_interval => ::IceCubeModel::Util.sanitize_integer_param(read_repeat_parameter(:repeat_interval)),
23
+ :repeat_year => ::IceCubeModel::Util.sanitize_integer_array_param(read_repeat_parameter(:repeat_year)),
24
+ :repeat_month => ::IceCubeModel::Util.sanitize_integer_array_param(read_repeat_parameter(:repeat_month)),
25
+ :repeat_day => ::IceCubeModel::Util.sanitize_integer_array_param(read_repeat_parameter(:repeat_day)),
26
+ :repeat_weekday => ::IceCubeModel::Util.sanitize_integer_array_param(read_repeat_parameter(:repeat_weekday))
27
+ }
28
+ end
29
+
30
+ module ClassMethods
31
+ def repeat_parameter_mappings
32
+ mappings_name = :@@repeat_parameter_mappings
33
+
34
+ unless class_variable_defined?(mappings_name)
35
+ class_variable_set(mappings_name, :repeat_start_date => :repeat_start_date,
36
+ :repeat_interval => :repeat_interval,
37
+ :repeat_year => :repeat_year,
38
+ :repeat_month => :repeat_month,
39
+ :repeat_day => :repeat_day,
40
+ :repeat_weekday => :repeat_weekday)
41
+ end
42
+
43
+ class_variable_get(mappings_name)
44
+ end
45
+
46
+ def with_repeat_param(param_name, replacement)
47
+ repeat_parameter_mappings[param_name] = method_name = "repeat #{param_name}"
48
+ if replacement.is_a?(::Proc)
49
+ define_method(method_name) do
50
+ replacement.call
51
+ end
52
+ else
53
+ define_method(method_name) do
54
+ send(replacement)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,50 @@
1
+ module IceCubeModel
2
+ module Util
3
+ def self.build_schedule(params)
4
+ schedule = ::IceCube::Schedule.new(::IceCubeModel::Util.sanitize_date_param(params[:repeat_start_date]))
5
+ schedule.add_recurrence_rule ::IceCubeModel::Util.build_cyclical_repeat_rules(params)
6
+
7
+ schedule
8
+ end
9
+
10
+ def self.build_cyclical_repeat_rules(params)
11
+ rule = ::IceCubeModel::Util.build_root_recurrence_rule(params)
12
+
13
+ rule = rule.month_of_year(*params[:repeat_month]) unless params[:repeat_month].blank?
14
+ rule = rule.day_of_month(*params[:repeat_day]) unless params[:repeat_day].blank?
15
+ rule = rule.day(*params[:repeat_weekday]) unless params[:repeat_weekday].blank?
16
+
17
+ rule
18
+ end
19
+
20
+ def self.build_root_recurrence_rule(params)
21
+ interval = params[:repeat_interval]
22
+ return ::IceCube::Rule.yearly(interval) unless params[:repeat_month].blank?
23
+ return ::IceCube::Rule.weekly(interval) unless params[:repeat_weekday].blank?
24
+
25
+ ::IceCube::Rule.monthly(interval)
26
+ end
27
+
28
+ def self.sanitize_date_param(date)
29
+ date = date.to_time(:utc) if date.is_a?(::Date) && !date.is_a?(::DateTime)
30
+ date = date.to_time.utc if date.is_a?(::DateTime)
31
+ date = Time.at(date).utc if date.is_a?(::Integer)
32
+
33
+ date
34
+ end
35
+
36
+ def self.sanitize_integer_array_param(param)
37
+ return nil if param.blank?
38
+ return param if param.is_a?(::Array)
39
+ return [param] if param.is_a?(::Integer)
40
+
41
+ param.split(',').map(&:to_i)
42
+ end
43
+
44
+ def self.sanitize_integer_param(param)
45
+ return 1 if param.blank?
46
+
47
+ param.to_i
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module IceCubeModel
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'ice_cube'
2
+
3
+ require 'active_support/core_ext/date'
4
+ require 'active_support/core_ext/integer'
5
+
6
+ require 'ice_cube_model/version'
7
+ require 'ice_cube_model/util'
8
+ require 'ice_cube_model/base'
@@ -0,0 +1,292 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::IceCubeModel do
4
+ class IceCubeObj
5
+ include ::IceCubeModel::Base
6
+
7
+ def initialize(options = {})
8
+ @options = {
9
+ :repeat_start_date => nil,
10
+ :repeat_interval => nil,
11
+ :repeat_year => nil,
12
+ :repeat_month => nil,
13
+ :repeat_day => nil,
14
+ :repeat_weekday => nil,
15
+ :repeat_week => nil
16
+ }.merge(options)
17
+ end
18
+
19
+ def method_missing(m, *_args, &_block)
20
+ return @options[m] if @options.key?(m)
21
+ fail ArgumentError, "Method `#{m}` doesn't exist."
22
+ end
23
+ end
24
+
25
+ describe 'monthly' do
26
+ let(:projector) do
27
+ ::IceCubeObj.new(
28
+ :repeat_start_date => ::Date.new(2015, 6, 1),
29
+ :repeat_day => '1'
30
+ )
31
+ end
32
+
33
+ it 'should emit [::Date]' do
34
+ results = projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 31))
35
+ expect(results).to eq([::Date.new(2015, 7, 1)])
36
+ expect(results[0]).to be_a(::Date)
37
+ end
38
+
39
+ it 'should render same day' do
40
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 1))).to eq([::Date.new(2015, 7, 1)])
41
+ end
42
+
43
+ it 'should render across multiple months' do
44
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 9, 1))).to eq([::Date.new(2015, 7, 1), ::Date.new(2015, 8, 1), ::Date.new(2015, 9, 1)])
45
+ end
46
+ end
47
+
48
+ describe 'twice monthly' do
49
+ let(:projector) do
50
+ ::IceCubeObj.new(
51
+ :repeat_start_date => ::Date.new(2015, 6, 1),
52
+ :repeat_day => '1,15'
53
+ )
54
+ end
55
+
56
+ it 'should render one month' do
57
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 31))).to eq([::Date.new(2015, 7, 1), ::Date.new(2015, 7, 15)])
58
+ end
59
+
60
+ it 'should render one month' do
61
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 8, 31))).to eq([::Date.new(2015, 7, 1), ::Date.new(2015, 7, 15), ::Date.new(2015, 8, 1), ::Date.new(2015, 8, 15)])
62
+ end
63
+ end
64
+
65
+ describe 'bi-monthly' do
66
+ let(:projector) do
67
+ ::IceCubeObj.new(
68
+ :repeat_start_date => ::Date.new(2015, 5, 1),
69
+ :repeat_day => '1',
70
+ :repeat_interval => '2'
71
+ )
72
+ end
73
+
74
+ it 'should render one month' do
75
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 7, 31))).to eq([::Date.new(2015, 7, 1)])
76
+ end
77
+
78
+ it 'should render multiple months' do
79
+ expect(projector.events_between(::Date.new(2015, 7, 1), ::Date.new(2015, 10, 31))).to eq([::Date.new(2015, 7, 1), ::Date.new(2015, 9, 1)])
80
+ end
81
+ end
82
+
83
+ describe 'every week' do
84
+ let(:projector) do
85
+ ::IceCubeObj.new(
86
+ :repeat_start_date => ::Date.new(2015, 7, 6),
87
+ :repeat_weekday => '1',
88
+ :repeat_day => nil
89
+ )
90
+ end
91
+
92
+ it 'should render one week' do
93
+ expect(
94
+ projector.events_between(
95
+ ::Date.new(2015, 7, 1),
96
+ ::Date.new(2015, 7, 7)
97
+ )
98
+ ).to eq([::Date.new(2015, 7, 6)])
99
+ end
100
+
101
+ it 'should render one month' do
102
+ expect(
103
+ projector.events_between(
104
+ ::Date.new(2015, 7, 1),
105
+ ::Date.new(2015, 8, 31)
106
+ )
107
+ ).to eq(
108
+ [
109
+ ::Date.new(2015, 7, 6),
110
+ ::Date.new(2015, 7, 13),
111
+ ::Date.new(2015, 7, 20),
112
+ ::Date.new(2015, 7, 27),
113
+ ::Date.new(2015, 8, 3),
114
+ ::Date.new(2015, 8, 10),
115
+ ::Date.new(2015, 8, 17),
116
+ ::Date.new(2015, 8, 24),
117
+ ::Date.new(2015, 8, 31)
118
+ ]
119
+ )
120
+ end
121
+ end
122
+
123
+ describe 'every two weeks' do
124
+ let(:projector) do
125
+ ::IceCubeObj.new(
126
+ :repeat_start_date => ::Date.new(2015, 7, 6),
127
+ :repeat_weekday => '1',
128
+ :repeat_day => nil,
129
+ :repeat_interval => '2'
130
+ )
131
+ end
132
+
133
+ it 'should render one week' do
134
+ expect(
135
+ projector.events_between(
136
+ ::Date.new(2015, 7, 1),
137
+ ::Date.new(2015, 7, 7)
138
+ )
139
+ ).to eq([::Date.new(2015, 7, 6)])
140
+ end
141
+
142
+ it 'should render one month' do
143
+ expect(
144
+ projector.events_between(
145
+ ::Date.new(2015, 7, 1),
146
+ ::Date.new(2015, 8, 31)
147
+ )
148
+ ).to eq(
149
+ [
150
+ ::Date.new(2015, 7, 6),
151
+ # ::Date.new(2015, 7, 13),
152
+ ::Date.new(2015, 7, 20),
153
+ # ::Date.new(2015, 7, 27),
154
+ ::Date.new(2015, 8, 3),
155
+ # ::Date.new(2015, 8, 10),
156
+ ::Date.new(2015, 8, 17),
157
+ # ::Date.new(2015, 8, 24),
158
+ ::Date.new(2015, 8, 31)
159
+ ]
160
+ )
161
+ end
162
+ end
163
+
164
+ describe 'annually' do
165
+ let(:projector) do
166
+ ::IceCubeObj.new(
167
+ :repeat_start_date => ::Date.new(2015, 1, 1),
168
+ :repeat_month => 2,
169
+ :repeat_day => 1
170
+ )
171
+ end
172
+
173
+ it 'should render one year' do
174
+ expect(projector.events_between(::Date.new(2015, 1, 1), ::Date.new(2015, 12, 31))).to eq([::Date.new(2015, 2, 1)])
175
+ end
176
+ end
177
+
178
+ describe 'bi-annually' do
179
+ let(:projector) do
180
+ ::IceCubeObj.new(
181
+ :repeat_start_date => ::Date.new(2015, 1, 1),
182
+ :repeat_interval => 2,
183
+ :repeat_month => 2,
184
+ :repeat_day => 1
185
+ )
186
+ end
187
+
188
+ it 'should render two year' do
189
+ expect(projector.events_between(::Date.new(2015, 1, 1), ::Date.new(2016, 12, 31))).to eq([::Date.new(2015, 2, 1)])
190
+ end
191
+ end
192
+
193
+ describe 'last weekday of month' do
194
+ context '31 day month' do
195
+ let(:projector) do
196
+ ::IceCubeObj.new(
197
+ :repeat_start_date => ::Date.new(2015, 1, 1),
198
+ :repeat_day => '31, 30, 29, 28, 27, 26, 25',
199
+ :repeat_weekday => 5
200
+ )
201
+ end
202
+
203
+ it 'should render' do
204
+ expect(projector.events_between(::Date.new(2015, 12, 1), ::Date.new(2015, 12, 31))).to eq([::Date.new(2015, 12, 25)])
205
+ end
206
+ end
207
+
208
+ context '31 day month' do
209
+ let(:projector) do
210
+ ::IceCubeObj.new(
211
+ :repeat_start_date => ::Date.new(2015, 1, 1),
212
+ :repeat_day => '31, 30, 29, 28, 27, 26, 25',
213
+ :repeat_weekday => 1
214
+ )
215
+ end
216
+
217
+ it 'should render' do
218
+ expect(projector.events_between(::Date.new(2016, 2, 1), ::Date.new(2016, 2, 29))).to eq([::Date.new(2016, 2, 29)])
219
+ end
220
+ end
221
+ end
222
+
223
+ describe 'handles ::DateTime as input' do
224
+ let(:projector) do
225
+ ::IceCubeObj.new(
226
+ :repeat_start_date => ::DateTime.new(2015, 5, 1),
227
+ :repeat_day => '1',
228
+ :repeat_interval => '2'
229
+ )
230
+ end
231
+
232
+ it 'should render one month' do
233
+ expect(projector.events_between(::DateTime.new(2015, 7, 1), ::DateTime.new(2015, 7, 31))).to eq([::DateTime.new(2015, 7, 1)])
234
+ end
235
+ end
236
+
237
+ describe 'handles integer (epoch) as input' do
238
+ let(:projector) do
239
+ ::IceCubeObj.new(
240
+ :repeat_start_date => 1_430_438_400, # Fri, 01 May 2015 00:00:00 GMT
241
+ :repeat_day => '1',
242
+ :repeat_interval => '2'
243
+ )
244
+ end
245
+
246
+ it 'should render one month' do
247
+ expect(projector.events_between(::DateTime.new(2015, 7, 1), ::DateTime.new(2015, 7, 31))).to eq([::DateTime.new(2015, 7, 1)])
248
+ end
249
+ end
250
+
251
+ describe '::ClassMethods' do
252
+ class IceCubeObjWithParameterMappings
253
+ include ::IceCubeModel::Base
254
+
255
+ with_repeat_param(:repeat_start_date, :start_date) # remap parameter to another
256
+ with_repeat_param(:repeat_interval, :interval) # remap parameter to a method
257
+ with_repeat_param(:repeat_day, -> { 1 }) # map parameter to lambda
258
+
259
+ def initialize(options = {})
260
+ @options = {
261
+ :start_date => nil,
262
+ :repeat_interval => nil,
263
+ :repeat_year => nil,
264
+ :repeat_month => nil,
265
+ :repeat_day => nil,
266
+ :repeat_weekday => nil,
267
+ :repeat_week => nil
268
+ }.merge(options)
269
+ end
270
+
271
+ def interval
272
+ 1
273
+ end
274
+
275
+ def method_missing(m, *_args, &_block)
276
+ return @options[m] if @options.key?(m)
277
+ fail ArgumentError, "Method `#{m}` doesn't exist."
278
+ end
279
+ end
280
+
281
+ let(:projector) do
282
+ ::IceCubeObjWithParameterMappings.new(
283
+ :start_date => ::Date.new(2015, 1, 1),
284
+ :repeat_month => 2
285
+ )
286
+ end
287
+
288
+ it 'should render correctly' do
289
+ expect(projector.events_between(::Date.new(2015, 1, 1), ::Date.new(2015, 12, 31))).to eq([::Date.new(2015, 2, 1)])
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe ::IceCubeModel::Util do
4
+ describe '#sanitize_date_param' do
5
+ it 'should handle epoch time' do
6
+ expect(::IceCubeModel::Util.sanitize_date_param(1_430_438_400)).to eq(DateTime.new(2015, 5, 1, 0, 0, 0))
7
+ end
8
+
9
+ it 'should handle date' do
10
+ expect(::IceCubeModel::Util.sanitize_date_param(Date.new(2015, 5, 1))).to eq(DateTime.new(2015, 5, 1, 0, 0, 0))
11
+ end
12
+
13
+ it 'should handle datetime' do
14
+ expect(::IceCubeModel::Util.sanitize_date_param(DateTime.new(2015, 5, 1, 0, 0, 0))).to eq(DateTime.new(2015, 5, 1, 0, 0, 0))
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+
20
+ require 'ice_cube_model'
21
+ require 'pry'
22
+
23
+ RSpec.configure do |config|
24
+ # rspec-expectations config goes here. You can use an alternate
25
+ # assertion/expectation library such as wrong or the stdlib/minitest
26
+ # assertions if you prefer.
27
+ config.expect_with :rspec do |expectations|
28
+ # This option will default to `true` in RSpec 4. It makes the `description`
29
+ # and `failure_message` of custom matchers include text for helper methods
30
+ # defined using `chain`, e.g.:
31
+ # be_bigger_than(2).and_smaller_than(4).description
32
+ # # => "be bigger than 2 and smaller than 4"
33
+ # ...rather than:
34
+ # # => "be bigger than 2"
35
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
36
+ end
37
+
38
+ # rspec-mocks config goes here. You can use an alternate test double
39
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
40
+ config.mock_with :rspec do |mocks|
41
+ # Prevents you from mocking or stubbing a method that does not exist on
42
+ # a real object. This is generally recommended, and will default to
43
+ # `true` in RSpec 4.
44
+ mocks.verify_partial_doubles = true
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,228 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ice_cube_model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Nichols
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ice_cube
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: spring
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: spring-commands-rspec
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ description: Add ice_cube methods to classes (e.g. active_record, active_model).
182
+ email:
183
+ - matt@nichols.name
184
+ executables: []
185
+ extensions: []
186
+ extra_rdoc_files: []
187
+ files:
188
+ - ".gitignore"
189
+ - ".rubocop.yml"
190
+ - Gemfile
191
+ - Gemfile.lock
192
+ - LICENSE
193
+ - README.md
194
+ - ice_cube_model.gemspec
195
+ - lib/ice_cube_model.rb
196
+ - lib/ice_cube_model/base.rb
197
+ - lib/ice_cube_model/util.rb
198
+ - lib/ice_cube_model/version.rb
199
+ - spec/lib/ice_cube_model_spec.rb
200
+ - spec/lib/util_spec.rb
201
+ - spec/spec_helper.rb
202
+ homepage: https://github.com/mattnichols/ice_cube_model
203
+ licenses: []
204
+ metadata: {}
205
+ post_install_message:
206
+ rdoc_options: []
207
+ require_paths:
208
+ - lib
209
+ required_ruby_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ required_rubygems_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ requirements: []
220
+ rubyforge_project:
221
+ rubygems_version: 2.4.6
222
+ signing_key:
223
+ specification_version: 4
224
+ summary: Extend any class with ice_cube (calendar repeating events) capabilities.
225
+ test_files:
226
+ - spec/lib/ice_cube_model_spec.rb
227
+ - spec/lib/util_spec.rb
228
+ - spec/spec_helper.rb