Commit 234cdc2c144326330b3a9f4e8ab319a7f4f91fce
Committed by
Andrew Kane
1 parent
166605c3
Exists in
master
and in
4 other branches
Allow custom model calculation methods (#150)
* Initial implementation of custom calculation methods * Use `#klass` to get the model * Move test to be database specific Custom model methods doesn't work specifically on Array objects * Simplify test case and example * Clean up methods to remove usage of `try` * Avoid floats by using a count calculation * Refactor checking of calculation methods Break into a separate class to avoid conflicting with existing ActiveRecord methods via `method_missing?` * Simplify test case by removing distinct * Add tests for alternate scenarios - calc method not white listed - calc method white listed but not defined * Specify a RuntimeError for Rails 4.0 tests
Showing
2 changed files
with
67 additions
and
4 deletions
Show diff stats
lib/groupdate/series.rb
... | ... | @@ -5,14 +5,14 @@ module Groupdate |
5 | 5 | def initialize(magic, relation) |
6 | 6 | @magic = magic |
7 | 7 | @relation = relation |
8 | + @calculations = Groupdate::Calculations.new(relation) | |
8 | 9 | end |
9 | 10 | |
10 | 11 | # clone to prevent modifying original variables |
11 | 12 | def method_missing(method, *args, &block) |
12 | - # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb | |
13 | - if ActiveRecord::Calculations.method_defined?(method) | |
13 | + if @calculations.include?(method) | |
14 | 14 | magic.perform(relation, method, *args, &block) |
15 | - elsif @relation.respond_to?(method, true) | |
15 | + elsif relation.respond_to?(method, true) | |
16 | 16 | Groupdate::Series.new(magic, relation.send(method, *args, &block)) |
17 | 17 | else |
18 | 18 | super |
... | ... | @@ -20,11 +20,36 @@ module Groupdate |
20 | 20 | end |
21 | 21 | |
22 | 22 | def respond_to?(method, include_all = false) |
23 | - ActiveRecord::Calculations.method_defined?(method) || relation.respond_to?(method) || super | |
23 | + @calculations.include?(method) || relation.respond_to?(method) || super | |
24 | 24 | end |
25 | 25 | |
26 | 26 | def reverse_order_value |
27 | 27 | nil |
28 | 28 | end |
29 | 29 | end |
30 | + | |
31 | + class Calculations | |
32 | + attr_reader :relation | |
33 | + | |
34 | + def initialize(relation) | |
35 | + @relation = relation | |
36 | + end | |
37 | + | |
38 | + def include?(method) | |
39 | + # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation/calculations.rb | |
40 | + ActiveRecord::Calculations.method_defined?(method) || custom_calculations.include?(method) | |
41 | + end | |
42 | + | |
43 | + def custom_calculations | |
44 | + return [] if !model.respond_to?(:groupdate_calculation_methods) | |
45 | + model.groupdate_calculation_methods | |
46 | + end | |
47 | + | |
48 | + private | |
49 | + | |
50 | + def model | |
51 | + return if !relation.respond_to?(:klass) | |
52 | + relation.klass | |
53 | + end | |
54 | + end | |
30 | 55 | end | ... | ... |
test/test_helper.rb
... | ... | @@ -18,6 +18,18 @@ ActiveRecord::Base.time_zone_aware_attributes = true |
18 | 18 | |
19 | 19 | class User < ActiveRecord::Base |
20 | 20 | has_many :posts |
21 | + | |
22 | + def self.groupdate_calculation_methods | |
23 | + [:custom_count, :undefined_calculation] | |
24 | + end | |
25 | + | |
26 | + def self.custom_count | |
27 | + count | |
28 | + end | |
29 | + | |
30 | + def self.unlisted_calculation | |
31 | + count | |
32 | + end | |
21 | 33 | end |
22 | 34 | |
23 | 35 | class Post < ActiveRecord::Base |
... | ... | @@ -346,6 +358,32 @@ module TestDatabase |
346 | 358 | assert_raises(ArgumentError) { User.group_by_day.first } |
347 | 359 | end |
348 | 360 | |
361 | + # custom model calculation methods | |
362 | + | |
363 | + def test_custom_model_calculation_method | |
364 | + create_user "2014-05-01", 1 | |
365 | + create_user "2014-05-01", 2 | |
366 | + create_user "2014-05-03", 3 | |
367 | + | |
368 | + expected = { | |
369 | + Date.parse("2014-05-01") => 2, | |
370 | + Date.parse("2014-05-02") => 0, | |
371 | + Date.parse("2014-05-03") => 1 | |
372 | + } | |
373 | + | |
374 | + assert_equal expected, User.group_by_day(:created_at).custom_count | |
375 | + end | |
376 | + | |
377 | + def test_using_unlisted_calculation_method_returns_new_series_instance | |
378 | + assert_instance_of Groupdate::Series, User.group_by_day(:created_at).unlisted_calculation | |
379 | + end | |
380 | + | |
381 | + def test_using_listed_but_undefined_custom_calculation_method_raises_error | |
382 | + assert_raises(RuntimeError) do | |
383 | + User.group_by_day(:created_at).undefined_calculation | |
384 | + end | |
385 | + end | |
386 | + | |
349 | 387 | private |
350 | 388 | |
351 | 389 | def call_method(method, field, options) | ... | ... |