flog 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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