flog 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,391 +0,0 @@
1
- module Centerstone #:nodoc:
2
- module Acts #:nodoc:
3
- module Range
4
-
5
- def self.included(base) # :nodoc:
6
- base.extend ClassMethods
7
- unless ActiveRecord::Base.respond_to?(:end_dated_association_date)
8
- class << ActiveRecord::Base
9
- attr :end_dated_association_date, true
10
- end
11
- ActiveRecord::Base.end_dated_association_date = Proc.new { Time.now }
12
- end
13
- end
14
-
15
- module InstanceMethods
16
- def self.included(base) # :nodoc:
17
- base.extend ClassMethods
18
- end
19
-
20
- %w{begin end}.each do |bound|
21
- define_method "acts_as_range_#{bound}" do
22
- send(self.class.send("acts_as_range_#{bound}_attr").to_sym)
23
- end
24
-
25
- define_method "acts_as_range_#{bound}=" do |val|
26
- send((self.class.send("acts_as_range_#{bound}_attr").to_s + '=').to_sym, val)
27
- end
28
- end
29
-
30
- # convert this object into a range
31
- # do something about nil/unspecified ends?
32
- def to_range
33
- begin_point = acts_as_range_begin
34
- end_point = acts_as_range_end
35
-
36
- begin_point ... end_point
37
- end
38
-
39
- # does the range for this object include the specified point?
40
- def include?(point)
41
- true if self.class.find(self.id, :on => point)
42
- rescue
43
- false
44
- end
45
-
46
- def contained_by?(range)
47
- if range.respond_to?(:acts_as_range_begin)
48
- # if this is really a range-ish object
49
- if range.acts_as_range_begin and range.acts_as_range_end
50
- # if it can actually be represented as a range
51
- return contained_by?(range.to_range)
52
- elsif range.acts_as_range_begin or range.acts_as_range_end
53
- # if at least one bound is defined, compare that bound
54
- if range.acts_as_range_begin
55
- return false unless acts_as_range_begin
56
- return acts_as_range_begin >= range.acts_as_range_begin
57
- end
58
-
59
- if range.acts_as_range_end
60
- return false unless acts_as_range_end
61
- return acts_as_range_end <= range.acts_as_range_end
62
- end
63
- else
64
- # the given "range" has no bounds, thus it contains all
65
- return true
66
- end
67
- end
68
-
69
- # if the given range includes both bounds, it contains this object
70
- # Note: Checking the end bound is a little tricky because of the exclusion choice with ranges (but not with acts_as_range)
71
- range.include?(acts_as_range_begin) and (range.include?(acts_as_range_end) or ((range.last == acts_as_range_end) and (range.exclude_end? == to_range.exclude_end?)))
72
- end
73
-
74
- def containing?(target)
75
- if target.respond_to?(:acts_as_range_begin)
76
- # if this is really a range-ish object
77
- # enough work has been done on contained_by?, so let's use that if we can
78
- target.contained_by?(self)
79
- else
80
- if target.is_a?(::Range) # core Range class, not this Range module
81
- if acts_as_range_begin and acts_as_range_end
82
- # if this object can actually be represented as a range
83
- # if this object's range includes both bounds of the given range, it contains that range
84
- # Note: Checking the end bound is a little tricky because of the exclusion choice with ranges (but not with acts_as_range)
85
- to_range.include?(target.first) and (to_range.include?(target.last) or ((to_range.last == target.last) and (to_range.exclude_end? == target.exclude_end?)))
86
- elsif acts_as_range_begin or acts_as_range_end
87
- # if at least one bound is defined, compare that bound
88
- if acts_as_range_begin
89
- return acts_as_range_begin <= target.first
90
- end
91
-
92
- if acts_as_range_end
93
- # note the problem with checking the end bound above
94
- return (acts_as_range_end > target.last or ((acts_as_range_end == target.last) and target.exclude_end?))
95
- end
96
- else
97
- # this object's "range" has no bounds, thus it contains all
98
- true
99
- end
100
- else
101
- # this is a single point, so check it
102
- begin_point = acts_as_range_begin || target
103
- end_point = acts_as_range_end || target
104
-
105
- (begin_point ... end_point).include?(target) or ((begin_point <= target) and !acts_as_range_end)
106
- end
107
- end
108
- end
109
- alias_method :contains?, :containing?
110
-
111
- def overlapping?(target)
112
- # contained by target or containing target
113
- return true if contained_by?(target) or containing?(target)
114
-
115
- # or containing either bound
116
- if target.respond_to?(:acts_as_range_begin)
117
- containing_begin = containing?(target.acts_as_range_begin) if target.acts_as_range_begin
118
- containing_end = containing?(target.acts_as_range_end) if target.acts_as_range_end
119
-
120
- return (containing_begin or containing_end)
121
- else
122
- if target.is_a?(::Range) # core Range class, not this Range module
123
- return (containing?(target.first) or containing?(target.last))
124
- end
125
- end
126
-
127
- # if it got this far
128
- false
129
- end
130
- alias_method :overlaps?, :overlapping?
131
-
132
- def before?(point)
133
- return false unless point
134
- return false unless acts_as_range_end
135
-
136
- if point.respond_to?(:acts_as_range_begin)
137
- return before?(point.acts_as_range_begin)
138
- end
139
-
140
- acts_as_range_end < point
141
- end
142
-
143
- def after?(point)
144
- return false unless point
145
- return false unless acts_as_range_begin
146
-
147
- if point.respond_to?(:acts_as_range_end)
148
- return after?(point.acts_as_range_end)
149
- end
150
-
151
- acts_as_range_begin > point
152
- end
153
-
154
- module ClassMethods
155
-
156
- %w{begin end}.each do |bound|
157
- define_method "acts_as_range_#{bound}_attr" do
158
- acts_as_range_configuration[bound.to_sym]
159
- end
160
- end
161
-
162
- # add new options to Foo.find:
163
- # :contain => t1 .. t2 - return objects whose spans contain this time interval
164
- # :containing => t1 .. t2 - return objects whose spans contain this time interval
165
- # :contained_by => t1 .. t2 - return objects whose spans are contained by this time interval
166
- # :overlapping => t1 .. t2 - return objects whose spans overlap this time interval
167
- # :on => t1 - return objects whose spans contain this time point
168
- # :before => t1 - return objects whose spans are completed on or before this time point
169
- # :after => t1 - return objects whose spans begin on or after this time point
170
- #
171
- # Note that each of the time interval methods will also take an object of this
172
- # class and will use the time interval from that object as search parameters.
173
- def find_with_range_restrictions(*args)
174
- original_args = args.dup
175
- options = extract_options_from_args!(args)
176
-
177
- # which new arguments do we recognize, and which scoping methods do they use?
178
- # eh, I don't like 'on' or 'contain'. Like the non-database instance methods
179
- # above, 'containing' could handle both cases of range and point
180
- method_map = { :contain => :with_containing_scope,
181
- :containing => :with_containing_scope,
182
- :contained_by => :with_contained_scope,
183
- :overlapping => :with_overlapping_scope,
184
- :on => nil,
185
- :before => nil,
186
- :after => nil }
187
-
188
- # find objects with time intervals containing this time point
189
- if options.has_key? :on
190
- return with_containing_scope(options[:on], options[:on]) do
191
- find_without_range_restrictions(*remove_args(original_args, method_map.keys))
192
- end
193
- end
194
-
195
- # find objects with time intervals containing this time point
196
- if options.has_key? :before
197
- return with_before_scope(options[:before]) do
198
- find_without_range_restrictions(*remove_args(original_args, method_map.keys))
199
- end
200
- end
201
-
202
- # find objects with time intervals containing this time point
203
- if options.has_key? :after
204
- return with_after_scope(options[:after]) do
205
- find_without_range_restrictions(*remove_args(original_args, method_map.keys))
206
- end
207
- end
208
-
209
- # otherwise, find objects with time intervals matching this range
210
- method_map.keys.each do |kind|
211
- if options.has_key? kind
212
- x = ranged_lookup(options[kind]) do |start, stop|
213
- self.send(method_map[kind], start, stop) do
214
- find_without_range_restrictions(*remove_args(original_args, method_map.keys))
215
- end
216
- end
217
- # Patch for find :first, :conditions => 'impossible'
218
- # Could be cleaner, but this handles it
219
- return x == [nil] ? nil : x
220
- end
221
- end
222
-
223
- # otherwise, find objects with time intervals active now
224
- if acts_as_range_configuration[:end_dated]
225
- with_current_time_scope { find_without_range_restrictions(*original_args) }
226
- else
227
- find_without_range_restrictions(*original_args)
228
- end
229
- end
230
-
231
- def count_with_range_restrictions(*args)
232
- if acts_as_range_configuration[:end_dated]
233
- with_current_time_scope { count_without_range_restrictions(*args) }
234
- else
235
- count_without_range_restrictions(*args)
236
- end
237
- end
238
-
239
- def calculate_with_range_restrictions(*args)
240
- if acts_as_range_configuration[:end_dated]
241
- with_current_time_scope { calculate_without_range_restrictions(*args) }
242
- else
243
- calculate_without_range_restrictions(*args)
244
- end
245
- end
246
-
247
- # break out an args list, add in new options, return a new args list
248
- def add_args(args, added)
249
- args << extract_options_from_args!(args).merge(added)
250
- end
251
-
252
- protected
253
-
254
- # break out an args list, remove specified options, return a new args list
255
- def remove_args(args, removed)
256
- options = extract_options_from_args!(args)
257
- removed.each {|k| options.delete(k)}
258
- args << options
259
- args.last.keys.length > 0 ? args : args.first
260
- end
261
-
262
- # provide for lookups on a date range /or/ an acts_as_range object (filtering object out)
263
- def ranged_lookup(obj)
264
- filter_object = obj.respond_to?(:acts_as_range_begin)
265
- start, stop = filter_object ? [obj.acts_as_range_begin, obj.acts_as_range_end] : [obj.first, obj.last]
266
- result = yield(start, stop)
267
- filter_object ? (Set.new(result) - Set.new([obj])).to_a : result
268
- end
269
-
270
- # find objects with intervals including the current time
271
- def with_current_time_scope(&block)
272
- t = ActiveRecord::Base.end_dated_association_date.call
273
- if t.respond_to? :first
274
- with_overlapping_scope(t.first, t.last, &block)
275
- else
276
- with_containing_scope(t, t, &block)
277
- end
278
- end
279
-
280
- # find objects which are entirely before the specified time
281
- def with_before_scope(t, &block)
282
- with_scope({:find => { :conditions => ["(#{table_name}.#{acts_as_range_end_attr} is not null and #{table_name}.#{acts_as_range_end_attr} < ?)", t ] } }, :merge, &block)
283
- end
284
-
285
- # find objects which are entirely after the specified time
286
- def with_after_scope(t, &block)
287
- with_scope({:find => { :conditions => ["(#{table_name}.#{acts_as_range_begin_attr} is not null and #{table_name}.#{acts_as_range_begin_attr} > ?)", t ] } }, :merge, &block)
288
- end
289
-
290
- # find objects with intervals contained by the interval t1 .. t2
291
- def with_contained_scope(t1, t2, &block)
292
- conditions = []
293
- args = []
294
-
295
- if t1.nil?
296
- if t2.nil?
297
- conditions << "(1=1)"
298
- else
299
- conditions << "(#{table_name}.#{acts_as_range_end_attr} is not NULL and #{table_name}.#{acts_as_range_end_attr} < ?)"
300
- args << t2
301
- end
302
- elsif t2.nil?
303
- conditions << "(#{table_name}.#{acts_as_range_begin_attr} is not NULL and #{table_name}.#{acts_as_range_begin_attr} >= ?)"
304
- args << t1
305
- else
306
- conditions << "(#{table_name}.#{acts_as_range_begin_attr} is not NULL and #{table_name}.#{acts_as_range_begin_attr} >= ?)"
307
- args << t1
308
- conditions << "(#{table_name}.#{acts_as_range_end_attr} is not NULL and #{table_name}.#{acts_as_range_end_attr} < ?)"
309
- args << t2
310
- end
311
-
312
- conditions = ([ conditions.join(' AND ') ] << args).flatten
313
- with_scope({:find => { :conditions => conditions } }, :merge, &block)
314
- end
315
-
316
- # find objects with intervals containing the interval t1 .. t2
317
- def with_containing_scope(t1, t2, &block)
318
- conditions = []
319
- args = []
320
-
321
- if t1.nil?
322
- conditions << "(#{table_name}.#{acts_as_range_begin_attr} is NULL)"
323
- else
324
- conditions << "(#{table_name}.#{acts_as_range_begin_attr} <= ? or #{table_name}.#{acts_as_range_begin_attr} IS NULL)"
325
- args << t1
326
- end
327
-
328
- if t2.nil?
329
- conditions << "(#{table_name}.#{acts_as_range_end_attr} is NULL)"
330
- else
331
- conditions << "(#{table_name}.#{acts_as_range_end_attr} > ? or #{table_name}.#{acts_as_range_end_attr} IS NULL)"
332
- args << t2
333
- end
334
-
335
- conditions = ([ conditions.join(' AND ') ] << args).flatten
336
- with_scope({:find => { :conditions => conditions } }, :merge, &block)
337
- end
338
-
339
- # find objects with intervals overlapping the interval t1 .. t2
340
- def with_overlapping_scope(t1, t2, &block)
341
- [with_containing_scope(t1, t1, &block)].flatten | [with_containing_scope(t2, t2, &block)].flatten | [with_contained_scope(t1, t2, &block)].flatten
342
- end
343
- end
344
- end
345
-
346
- module ClassMethods
347
- def acts_as_range(options = {})
348
- return if acts_as_range? # don't let this be done twice
349
- class_inheritable_reader :acts_as_range_configuration
350
- raise "options must be a Hash" unless options.is_a?(Hash)
351
-
352
- acts_as_range_configure_class({ :begin => :begin, :end => :end }.update(options))
353
- end
354
-
355
- def acts_as_range?
356
- included_modules.include?(InstanceMethods)
357
- end
358
-
359
- # ensure that the beginning of the interval does not follow its end
360
- def validates_interval
361
- configuration = { :message => "#{acts_as_range_configuration[:begin].to_s.humanize} must be before #{acts_as_range_configuration[:end].to_s.humanize}.", :on => [ :save, :update ] }
362
- configuration[:on].each do |symbol|
363
- send(validation_method(symbol)) do |record|
364
- unless configuration[:if] && !evaluate_condition(configuration[:if], record)
365
- start, stop = record.acts_as_range_begin, record.acts_as_range_end
366
- unless start.nil? or stop.nil? or start <= stop
367
- record.errors.add(acts_as_range_configuration[:begin], configuration[:message])
368
- end
369
- end
370
- end
371
- end
372
- end
373
-
374
- protected
375
-
376
- def acts_as_range_configure_class(options = {})
377
- include InstanceMethods
378
- write_inheritable_attribute(:acts_as_range_configuration, options)
379
-
380
- class << self
381
- %w{find count calculate}.each do |method|
382
- alias_method_chain method.to_sym, :range_restrictions
383
- end
384
- end
385
-
386
- validates_interval
387
- end
388
- end
389
- end
390
- end
391
- end
@@ -1,11 +0,0 @@
1
- module Centerstone #:nodoc:
2
- module AssociationExtensions
3
- module DateRanged
4
-
5
- def current
6
- containing(Time.now)
7
- end
8
-
9
- end
10
- end
11
- end
@@ -1,13 +0,0 @@
1
- module Centerstone #:nodoc:
2
- module AssociationExtensions
3
- module Ranged
4
-
5
- %w{before after containing contained_by overlapping}.each do |comparison|
6
- define_method comparison do |target|
7
- self.select { |x| x.send((comparison + '?').to_sym, target) }
8
- end
9
- end
10
-
11
- end
12
- end
13
- end
@@ -1,50 +0,0 @@
1
- module Centerstone #:nodoc:
2
- module ReflectionExtensions
3
- module Ranged
4
-
5
- def self.included(base)
6
- puts 'hello'
7
- base.send(:include, InstanceMethods)
8
-
9
- base.instance_eval do
10
- alias_method_chain :initialize, :has_many_range_extension
11
- end
12
- end
13
-
14
- module InstanceMethods
15
- def initialize_with_has_many_range_extension(*args)
16
- puts 'yo'
17
- returning initialize_without_has_many_range_extension(*args) do
18
- puts 'returning stuff'
19
- if macro.to_s == 'has_many'
20
- puts 'adding the extension'
21
- add_has_many_range_extension
22
- end
23
- puts 'blah'
24
- end
25
- end
26
-
27
- private
28
-
29
- def add_has_many_range_extension
30
- puts 'extension adding, hey'
31
- puts 'bbbbb'
32
- puts "target class [#{klass}]"
33
- if klass.acts_as_range?
34
- puts 'target acts as range'
35
- extension = Centerstone::AssociationExtensions::Ranged
36
- opts = options
37
-
38
- opts[:extend] ||= []
39
- opts[:extend] = [opts[:extend]].flatten
40
-
41
- opts[:extend].push(extension) unless opts[:extend].include?(extension)
42
-
43
- @options = opts
44
- end
45
- end
46
- end
47
-
48
- end
49
- end
50
- end