mochigome 0.1.14 → 0.1.15

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -5,5 +5,6 @@
5
5
  - Named subsets of different fields and agg fields on a single model
6
6
  - Some kind of single-page preview on the edit screen would be cool. Maybe use FOP with fake data and the PNG output option?
7
7
  - Automatically set default to 0 on sum and count aggregation
8
- - Treat through-associations as hints for preferred paths
9
8
  - Better handling of nil in subgroup fields
9
+ - Handle has_and_belongs_to_many correctly
10
+ - Deal with ignore_assoc properly in both directions and when we see :through assocs
data/lib/mochigome_ver.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mochigome
2
- VERSION = "0.1.14"
2
+ VERSION = "0.1.15"
3
3
  end
@@ -6,9 +6,9 @@ module Mochigome
6
6
  base.write_inheritable_attribute :mochigome_focus_settings, nil
7
7
  base.class_inheritable_reader :mochigome_focus_settings
8
8
 
9
- # TODO: Use an ordered hash for this
10
- base.write_inheritable_attribute :mochigome_aggregation_settings, nil
11
- base.class_inheritable_reader :mochigome_aggregation_settings
9
+ # FIXME: Unclear on how this interacts with inheritance...
10
+ base.write_inheritable_attribute :mochigome_aggregation_settings_sets, {}
11
+ base.class_inheritable_reader :mochigome_aggregation_settings_sets
12
12
  end
13
13
 
14
14
  module ClassMethods
@@ -45,13 +45,18 @@ module Mochigome
45
45
 
46
46
  # TODO: Split out aggregation stuff into its own module
47
47
 
48
- def has_mochigome_aggregations
49
- if self.try(:mochigome_aggregation_settings).try(:model) == self
50
- raise Mochigome::ModelSetupError.new("Already aggregation settings for #{self.name}")
48
+ def has_mochigome_aggregations(name = :default)
49
+ name = name.to_sym
50
+ if mochigome_aggregation_settings_sets[name].try(:model) == self
51
+ raise Mochigome::ModelSetupError.new("Can't overwrite aggregation settings for #{self.name}")
51
52
  end
52
53
  settings = AggregationSettings.new(self)
53
54
  yield settings if block_given?
54
- write_inheritable_attribute :mochigome_aggregation_settings, settings
55
+ mochigome_aggregation_settings_sets[name] = settings
56
+ end
57
+
58
+ def mochigome_aggregation_settings(name = :default)
59
+ mochigome_aggregation_settings_sets[name]
55
60
  end
56
61
 
57
62
  def assoc_condition(name)
data/lib/query.rb CHANGED
@@ -17,9 +17,14 @@ module Mochigome
17
17
 
18
18
  @aggregate_rels = ActiveSupport::OrderedHash.new
19
19
  aggregate_sources.each do |a|
20
- focus_model, data_model = case a
21
- when Array then [a.first, a.second]
22
- else [a, a]
20
+ focus_model, data_model, agg_setting_name = nil, nil, nil
21
+ if a.is_a?(Array) then
22
+ focus_model = a.select{|e| e.is_a?(Class)}.first
23
+ data_model = a.select{|e| e.is_a?(Class)}.last
24
+ agg_setting_name = a.select{|e| e.is_a?(Symbol)}.first || :default
25
+ else
26
+ focus_model = data_model = a
27
+ agg_setting_name = :default
23
28
  end
24
29
 
25
30
  agg_rel = Relation.new(@layer_types)
@@ -28,15 +33,21 @@ module Mochigome
28
33
 
29
34
  key_cols = @ids_rel.spine_layers.map{|m| m.arel_primary_key}
30
35
 
31
- agg_fields = data_model.mochigome_aggregation_settings.
36
+ agg_fields = data_model.
37
+ mochigome_aggregation_settings(agg_setting_name).
32
38
  options[:fields].reject{|a| a[:in_ruby]}
33
39
  agg_fields.each_with_index do |a, i|
34
40
  d_expr = a[:value_proc].call(data_model.arel_table)
35
41
  agg_rel.select_expr(d_expr.as("d%03u" % i))
36
42
  end
37
43
 
38
- @aggregate_rels[focus_model] ||= ActiveSupport::OrderedHash.new
39
- @aggregate_rels[focus_model][data_model] = (0..key_cols.length).map{|n|
44
+ agg_rel_key = {
45
+ :focus_model => focus_model,
46
+ :data_model => data_model,
47
+ :agg_setting_name => agg_setting_name
48
+ }
49
+
50
+ @aggregate_rels[agg_rel_key] = (0..key_cols.length).map{|n|
40
51
  lambda {|cond|
41
52
  data_rel = agg_rel.clone
42
53
  data_rel.apply_condition(cond)
@@ -134,55 +145,53 @@ module Mochigome
134
145
  end
135
146
 
136
147
  def load_aggregate_data(node, cond)
137
- @aggregate_rels.each do |focus_model, data_model_rels|
138
- # TODO Actually get the key types found in init for this aggregation
139
- super_types = @layer_types.take_while{|m| m != focus_model}
140
- data_model_rels.each do |data_model, rel_funcs|
141
- aggs = data_model.mochigome_aggregation_settings.options[:fields]
142
- aggs_count = aggs.reject{|a| a[:in_ruby]}.size
143
- rel_funcs.each do |rel_func|
144
- q = rel_func.call(cond)
145
- data_tree = {}
146
- @layer_types.first.connection.select_all(q.to_sql).each do |row|
147
- group_values = row.keys.select{|k| k.start_with?("g")}.sort.map{|k| row[k]}
148
- data_values = row.keys.select{|k| k.start_with?("d")}.sort.map{|k| row[k]}
149
- if group_values.empty?
150
- data_tree = data_values
151
- else
152
- c = data_tree
153
- group_values.take(group_values.size-1).each do |group_id|
154
- c = (c[group_id] ||= {})
155
- end
156
- c[group_values.last] = data_values
148
+ @aggregate_rels.each do |key, rel_funcs|
149
+ data_model = key[:data_model]
150
+ agg_name = key[:agg_setting_name]
151
+ agg_settings = data_model.mochigome_aggregation_settings(agg_name)
152
+
153
+ rel_funcs.each do |rel_func|
154
+ q = rel_func.call(cond)
155
+ data_tree = {}
156
+ @layer_types.first.connection.select_all(q.to_sql).each do |row|
157
+ group_values = row.keys.select{|k| k.start_with?("g")}.sort.map{|k| row[k]}
158
+ data_values = row.keys.select{|k| k.start_with?("d")}.sort.map{|k| row[k]}
159
+ if group_values.empty?
160
+ data_tree = data_values
161
+ else
162
+ c = data_tree
163
+ group_values.take(group_values.size-1).each do |group_id|
164
+ c = (c[group_id] ||= {})
157
165
  end
166
+ c[group_values.last] = data_values
158
167
  end
159
- insert_aggregate_data_fields(node, data_tree, data_model)
160
168
  end
169
+ insert_aggregate_data_fields(node, data_tree, agg_settings)
161
170
  end
162
171
  end
163
172
  end
164
173
 
165
- def insert_aggregate_data_fields(node, table, data_model)
174
+ def insert_aggregate_data_fields(node, table, agg_settings)
166
175
  if table.is_a? Array
167
- fields = data_model.mochigome_aggregation_settings.options[:fields]
176
+ fields = agg_settings.options[:fields]
168
177
  # Pre-fill the node with all fields in the right order
169
- fields.each{|agg| node[agg[:name]] = agg[:default] unless agg[:hidden] }
170
- agg_row = {} # Hold regular aggs here to be used in ruby-based aggs
171
- fields.reject{|agg| agg[:in_ruby]}.zip(table).each do |agg, v|
172
- v ||= agg[:default]
173
- agg_row[agg[:name]] = v
174
- node[agg[:name]] = v unless agg[:hidden]
178
+ fields.each{|fld| node[fld[:name]] = fld[:default] unless fld[:hidden] }
179
+ agg_row = {} # Hold regular results here to be used in ruby-based fields
180
+ fields.reject{|fld| fld[:in_ruby]}.zip(table).each do |fld, v|
181
+ v ||= fld[:default]
182
+ agg_row[fld[:name]] = v
183
+ node[fld[:name]] = v unless fld[:hidden]
175
184
  end
176
- fields.select{|agg| agg[:in_ruby]}.each do |agg|
177
- node[agg[:name]] = agg[:ruby_proc].call(agg_row)
185
+ fields.select{|fld| fld[:in_ruby]}.each do |fld|
186
+ node[fld[:name]] = fld[:ruby_proc].call(agg_row)
178
187
  end
179
188
  node.children.each do |c|
180
- insert_aggregate_data_fields(c, [], data_model)
189
+ insert_aggregate_data_fields(c, [], agg_settings)
181
190
  end
182
191
  else
183
192
  node.children.each do |c|
184
193
  subtable = table[c[:id]] || []
185
- insert_aggregate_data_fields(c, subtable, data_model)
194
+ insert_aggregate_data_fields(c, subtable, agg_settings)
186
195
  end
187
196
  end
188
197
  end
@@ -9,6 +9,7 @@ class Product < ActiveRecord::Base
9
9
  "Consonant"
10
10
  ).call(Product.arel_table)
11
11
  end
12
+
12
13
  has_mochigome_aggregations do |a|
13
14
  a.fields [
14
15
  :sum_price,
@@ -24,6 +25,10 @@ class Product < ActiveRecord::Base
24
25
  a.fields_in_ruby [ {"Count squared" => lambda{|row| row["Secret count"].try(:**, 2)}} ]
25
26
  end
26
27
 
28
+ has_mochigome_aggregations(:averaging) do |a|
29
+ a.fields [:average_price]
30
+ end
31
+
27
32
  belongs_to :category
28
33
  has_many :store_products
29
34
  has_many :stores, :through => :store_products
@@ -275,6 +275,15 @@ describe Mochigome::Query do
275
275
  assert_equal (2*@product_c.price).to_f.to_s, (data_node/1/0/0)['Gross'].to_f.to_s
276
276
  end
277
277
 
278
+ it "can use a named aggregate data setting" do
279
+ q = Mochigome::Query.new(
280
+ [Owner],
281
+ :aggregate_sources => [[Product, :averaging]]
282
+ )
283
+ data_node = q.run
284
+ assert_equal ((19.95*4 + 5)/5).to_f.to_s, data_node['Products average price'].to_f.to_s
285
+ end
286
+
278
287
  it "collects aggregate data in subgroups" do
279
288
  q = Mochigome::Query.new(
280
289
  [Mochigome::SubgroupModel.new(Owner, :last_name), Owner, Store, Product],
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mochigome
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 5
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 14
10
- version: 0.1.14
9
+ - 15
10
+ version: 0.1.15
11
11
  platform: ruby
12
12
  authors:
13
13
  - David Mike Simon
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-06-03 00:00:00 Z
18
+ date: 2012-06-04 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  version_requirements: &id001 !ruby/object:Gem::Requirement