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,14 +5,14 @@ module Groupdate | ||
5 | def initialize(magic, relation) | 5 | def initialize(magic, relation) |
6 | @magic = magic | 6 | @magic = magic |
7 | @relation = relation | 7 | @relation = relation |
8 | + @calculations = Groupdate::Calculations.new(relation) | ||
8 | end | 9 | end |
9 | 10 | ||
10 | # clone to prevent modifying original variables | 11 | # clone to prevent modifying original variables |
11 | def method_missing(method, *args, &block) | 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 | magic.perform(relation, method, *args, &block) | 14 | magic.perform(relation, method, *args, &block) |
15 | - elsif @relation.respond_to?(method, true) | 15 | + elsif relation.respond_to?(method, true) |
16 | Groupdate::Series.new(magic, relation.send(method, *args, &block)) | 16 | Groupdate::Series.new(magic, relation.send(method, *args, &block)) |
17 | else | 17 | else |
18 | super | 18 | super |
@@ -20,11 +20,36 @@ module Groupdate | @@ -20,11 +20,36 @@ module Groupdate | ||
20 | end | 20 | end |
21 | 21 | ||
22 | def respond_to?(method, include_all = false) | 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 | end | 24 | end |
25 | 25 | ||
26 | def reverse_order_value | 26 | def reverse_order_value |
27 | nil | 27 | nil |
28 | end | 28 | end |
29 | end | 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 | end | 55 | end |
test/test_helper.rb
@@ -18,6 +18,18 @@ ActiveRecord::Base.time_zone_aware_attributes = true | @@ -18,6 +18,18 @@ ActiveRecord::Base.time_zone_aware_attributes = true | ||
18 | 18 | ||
19 | class User < ActiveRecord::Base | 19 | class User < ActiveRecord::Base |
20 | has_many :posts | 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 | end | 33 | end |
22 | 34 | ||
23 | class Post < ActiveRecord::Base | 35 | class Post < ActiveRecord::Base |
@@ -346,6 +358,32 @@ module TestDatabase | @@ -346,6 +358,32 @@ module TestDatabase | ||
346 | assert_raises(ArgumentError) { User.group_by_day.first } | 358 | assert_raises(ArgumentError) { User.group_by_day.first } |
347 | end | 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 | private | 387 | private |
350 | 388 | ||
351 | def call_method(method, field, options) | 389 | def call_method(method, field, options) |