calculable_attrs 0.0.8 → 0.0.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2cb90fc5396803596c76b7179e003580f5325a57
4
- data.tar.gz: a6d5fa763da75d215749063635f0aec3a787c5ea
3
+ metadata.gz: 57c06a0f4f9704dd33c80b66e571b32df767ad3d
4
+ data.tar.gz: 77a6b0903406d4f36c197fa0db1ae1f7fedfadf4
5
5
  SHA512:
6
- metadata.gz: 8cd92421d55e7e32b2f742721851f7d2285165a5be5db5066195669e7d03bfa31e78aa767a4b224ade25939ed3b0b48491d7ddad984a35d1bfe45a280206eeed
7
- data.tar.gz: 07fc087b96843bc8be86ff3053a1f06226c2806a86b721a1722e1fb790ecdeba34917b236ac2a0ca998ff298f0527f0a3217e643542ded97a78870ce4e9aca7b
6
+ metadata.gz: ebbfb7711a299a2b597a9ef419ee4af4bf2c1592636e30a7734063eb3811d6c20421a0137170c758f04fcb910cfff344e2c68c6b215ca247b01a4a2910a1721f
7
+ data.tar.gz: 6e6df74cae0281b0ed8d8d9d9e92a262106b3d813c63bce4faac7bec583fbd33d75bb38b369519c1b398fdd18753d9f3e2d1300c39c815661b4ccdcceca1b602
@@ -1,3 +1,3 @@
1
1
  module CalculableAttrs::ActiveRecord::Querying
2
- delegate :calculate_attrs, to: :all
2
+ delegate :calculate_attrs, :includes_calculable_attrs, :joins_calculable_attrs, to: :all
3
3
  end
@@ -1,25 +1,48 @@
1
1
  module CalculableAttrs::ActiveRecord::Relation
2
- def calculate_attrs(*attrs)
3
- spawn.calculate_attrs!(*attrs)
4
- end
2
+ JOINED_RELATION_NAME = 'calculated_attrs'
5
3
 
6
- def calculate_attrs!(*attrs)
7
- attrs.reject!(&:blank?)
8
- attrs.flatten!
9
-
10
- attrs = [true] if attrs.empty?
4
+ def includes_calculable_attrs(*attrs)
5
+ spawn.includes_calculable_attrs!(*attrs)
6
+ end
7
+ alias_method :calculate_attrs, :includes_calculable_attrs
11
8
 
12
- self.calculate_attrs_values |= attrs
9
+ def includes_calculable_attrs!(*attrs)
10
+ set_calculable_attrs_included(refine_calculable_attrs(attrs))
13
11
  self
14
12
  end
13
+ alias_method :calculate_attrs!, :includes_calculable_attrs!
15
14
 
16
- def calculate_attrs_values
17
- @values[:calculate_attrs] || []
15
+ def joins_calculable_attrs(*attrs)
16
+ spawn.joins_calculable_attrs!(*attrs)
18
17
  end
19
18
 
20
- def calculate_attrs_values=(values)
21
- raise ImmutableRelation if @loaded
22
- @values[:calculate_attrs] = values
19
+ def joins_calculable_attrs!(*attrs)
20
+ set_calculable_attrs_joined(refine_calculable_attrs(attrs))
21
+
22
+ scope = CalculableAttrs::ModelCalculableAttrsScope.new(klass)
23
+ scope.add_attrs(calculable_attrs_joined)
24
+ _select!("#{ klass.table_name }.*")
25
+ scope.calculators_to_use.each_with_index do |calcualtor, index|
26
+ joined_relation_name = '__' + JOINED_RELATION_NAME + '_' + index.to_s + '__'
27
+
28
+ attrs_to_calculate = scope.attrs && calcualtor.attrs
29
+ left_join_sql =
30
+ <<-sql
31
+ LEFT JOIN ( #{ calcualtor.query_with_grouping(attrs_to_calculate, nil).to_sql })
32
+ AS #{ joined_relation_name }
33
+ ON #{ joined_relation_name }.#{ calcualtor.calculable_foreign_key } = #{ klass.table_name }.id
34
+ sql
35
+
36
+ calculable_values_sql = attrs_to_calculate.map do |attr|
37
+ attr_name = "#{ klass.name.underscore }_#{ attr }"
38
+ left_join_sql.sub!(" AS #{ attr }", " AS #{ attr_name }")
39
+ klass.send(:sanitize_sql, ["COALESCE(#{ joined_relation_name }.#{ attr_name }, ?) AS #{ attr_name }", calcualtor.default(attr)])
40
+ end.join(',')
41
+
42
+ joins!(left_join_sql)
43
+ _select!(calculable_values_sql)
44
+ end
45
+ self
23
46
  end
24
47
 
25
48
 
@@ -36,36 +59,68 @@ module CalculableAttrs::ActiveRecord::Relation
36
59
 
37
60
  private
38
61
 
62
+ def refine_calculable_attrs(attrs)
63
+ attrs.reject!(&:blank?)
64
+ attrs.flatten!
65
+ attrs = [true] if attrs.empty?
66
+ attrs
67
+ end
68
+
69
+ def calculable_attrs_joined
70
+ @values[:calculable_attrs_joined] || []
71
+ end
72
+
73
+ def set_calculable_attrs_joined(values)
74
+ raise ImmutableRelation if @loaded
75
+ @values[:calculable_attrs_joined] = [] unless @values[:calculable_attrs_joined]
76
+ @values[:calculable_attrs_joined] |= values
77
+ end
78
+
79
+ def calculable_attrs_included
80
+ @values[:calculable_attrs_included] || []
81
+ end
82
+
83
+ def set_calculable_attrs_included(values)
84
+ raise ImmutableRelation if @loaded
85
+ @values[:calculable_attrs_included] = [] unless @values[:calculable_attrs_included]
86
+ @values[:calculable_attrs_included] |= values
87
+ end
88
+
39
89
  def append_calculable_attrs
40
- unless calculate_attrs_values.empty?
41
- models_calculable_scopes= {}
42
- collect_models_calculable_attrs(models_calculable_scopes, klass, calculate_attrs_values)
43
- models_calculable_scopes = models_calculable_scopes.select { |model, scope| scope.has_attrs }
44
- collect_models_ids(models_calculable_scopes, @records, calculate_attrs_values)
45
- models_calculable_scopes.each { |model, scope| scope.calculate }
46
- put_calculated_values(models_calculable_scopes, @records, calculate_attrs_values)
90
+ unless calculable_attrs_included.empty?
91
+ attrs = calculable_attrs_included - calculable_attrs_joined
92
+ models_calculable_scopes = collect_calculable_scopes(attrs)
93
+ collect_models_ids(models_calculable_scopes, @records, attrs)
94
+ models_calculable_scopes.values.each { |scope| scope.calculate }
95
+ put_calculated_values(models_calculable_scopes, @records, attrs)
96
+ put_joined_calculated_values(@records)
47
97
  end
48
98
 
49
99
  @records
50
100
  end
51
101
 
102
+ def collect_calculable_scopes(attrs)
103
+ models_calculable_scopes= {}
104
+ collect_models_calculable_attrs(models_calculable_scopes, klass, attrs)
105
+ models_calculable_scopes.select { |model, scope| scope.has_attrs }
106
+ end
107
+
52
108
  def collect_models_calculable_attrs(models_calculable_scopes, klass, attrs_to_calculate)
53
109
  attrs_to_calculate = [attrs_to_calculate] unless attrs_to_calculate.is_a?(Array)
54
110
  scope = (models_calculable_scopes[klass] ||= CalculableAttrs::ModelCalculableAttrsScope.new(klass))
55
111
  attrs_to_calculate.each do |attrs_to_calculate_item|
56
-
57
- case attrs_to_calculate_item
58
- when Symbol
59
- if klass.reflect_on_association(attrs_to_calculate_item)
60
- collect_association_calculable_attrs(models_calculable_scopes, klass, attrs_to_calculate_item, true)
61
- else
62
- scope.add_attr(attrs_to_calculate_item)
63
- end
64
- when true
65
- scope.add_all_attrs
66
- when Hash
67
- attrs_to_calculate_item.each do |association_name, association_attrs_to_calculate|
68
- collect_association_calculable_attrs(models_calculable_scopes, klass, association_name, association_attrs_to_calculate)
112
+ case attrs_to_calculate_item
113
+ when Symbol
114
+ if klass.reflect_on_association(attrs_to_calculate_item)
115
+ collect_association_calculable_attrs(models_calculable_scopes, klass, attrs_to_calculate_item, true)
116
+ else
117
+ scope.add_attr(attrs_to_calculate_item)
118
+ end
119
+ when true
120
+ scope.add_all_attrs
121
+ when Hash
122
+ attrs_to_calculate_item.each do |association_name, association_attrs_to_calculate|
123
+ collect_association_calculable_attrs(models_calculable_scopes, klass, association_name, association_attrs_to_calculate)
69
124
  end
70
125
  end
71
126
  end
@@ -93,6 +148,20 @@ module CalculableAttrs::ActiveRecord::Relation
93
148
  end
94
149
  end
95
150
 
151
+ def put_joined_calculated_values(records)
152
+ joined_attrs = calculable_attrs_joined
153
+ joined_attrs = klass.calculable_attrs if joined_attrs == [true]
154
+ unless joined_attrs.empty?
155
+ names_hash = joined_attrs.map {|attr| [attr, "#{ klass.name.underscore }_#{ attr }"]}.to_h
156
+ records.each do |record|
157
+ names_hash.each do |attr_name, attr_full_name|
158
+ value = record.send(attr_full_name)
159
+ record.set_calculable_attr_value(attr_name, value)
160
+ end
161
+ end
162
+ end
163
+ end
164
+
96
165
  def iterate_scoped_records_recursively(models_calculable_scopes, records, attrs_to_calculate, &block)
97
166
  iterate_records_recursively(records, attrs_to_calculate) do |record|
98
167
  scope = models_calculable_scopes[record.class]
@@ -1,6 +1,6 @@
1
1
  class CalculableAttrs::Calculator
2
2
  CALCULABLE_FOREIGN_KEY = '__calculable_id__'
3
- attr_reader :attrs, :relation, :foreign_key
3
+ attr_reader :attrs, :relation, :foreign_key, :defaults
4
4
 
5
5
  def initialize(relation: nil, foreign_key: nil, attributes: nil)
6
6
  @relation = relation
@@ -25,12 +25,20 @@ class CalculableAttrs::Calculator
25
25
  end
26
26
  end
27
27
 
28
+ def calculable_foreign_key
29
+ CALCULABLE_FOREIGN_KEY
30
+ end
31
+
28
32
  def calculate_many(attrs, ids)
29
- query = base_query(attrs, ids).select("#{ @foreign_key } AS #{ CALCULABLE_FOREIGN_KEY }").group(@foreign_key)
33
+ query = query_with_grouping(attrs, ids)
30
34
  records = query.load
31
35
  normalize_many_records_result(ids, attrs, records)
32
36
  end
33
37
 
38
+ def query_with_grouping(attrs, ids)
39
+ base_query(attrs, ids).select("#{ @foreign_key } AS #{ calculable_foreign_key }").group(@foreign_key)
40
+ end
41
+
34
42
  def calculate_one(attrs, id)
35
43
  record = base_query(attrs, id).load.first
36
44
  normalize_one_record_result(attrs, record)
@@ -45,7 +53,15 @@ class CalculableAttrs::Calculator
45
53
  end
46
54
 
47
55
  def scoped_relation(id)
48
- @relation.call.where( @foreign_key => id)
56
+ if id
57
+ @relation.call.where( @foreign_key => id)
58
+ else
59
+ @relation.call
60
+ end
61
+ end
62
+
63
+ def default(attr)
64
+ @defaults.key?(attr) ? @defaults[attr] : 0
49
65
  end
50
66
 
51
67
  private
@@ -80,7 +96,7 @@ class CalculableAttrs::Calculator
80
96
  end
81
97
 
82
98
  def normalize_one_record_result(attrs, record)
83
- attrs.map { |a| [a, record.try(a) || ( @defaults.key?(a) ? @defaults[a] : 0)] }.to_h
99
+ attrs.map { |a| [a, record.try(a) || default(a)] }.to_h
84
100
  end
85
101
 
86
102
 
@@ -7,6 +7,14 @@ class CalculableAttrs::ModelCalculableAttrsScope
7
7
  @ids = []
8
8
  end
9
9
 
10
+ def add_attrs(attrs)
11
+ if attrs == true || attrs == [ true ]
12
+ add_all_attrs
13
+ else
14
+ attrs.each { |attr| add_attr(attr) }
15
+ end
16
+ end
17
+
10
18
  def add_attr(attribute)
11
19
  attribute = attribute.to_sym
12
20
  @attrs.push(attribute) unless @attrs.include?(attribute)
@@ -37,6 +45,15 @@ class CalculableAttrs::ModelCalculableAttrsScope
37
45
  end
38
46
 
39
47
 
48
+ def calculators_to_use
49
+ calculators_to_use = []
50
+ @attrs.each do |attribute|
51
+ calculator = @model.calculable_attrs_calculators[attribute]
52
+ calculators_to_use.push(calculator) unless calculators_to_use.include?(calculator)
53
+ end
54
+ calculators_to_use
55
+ end
56
+
40
57
  private
41
58
 
42
59
  def merge_calculated_values(calculated_values)
@@ -47,12 +64,5 @@ class CalculableAttrs::ModelCalculableAttrsScope
47
64
  end
48
65
  end
49
66
 
50
- def calculators_to_use
51
- calculators_to_use = []
52
- @attrs.each do |attribute|
53
- calculator = @model.calculable_attrs_calculators[attribute]
54
- calculators_to_use.push(calculator) unless calculators_to_use.include?(calculator)
55
- end
56
- calculators_to_use
57
- end
67
+
58
68
  end
@@ -1,3 +1,3 @@
1
1
  module CalculableAttrs
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.9"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calculable_attrs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Sharkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-26 00:00:00.000000000 Z
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: sqlite3
42
+ name: pg
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="