mochigome 0.0.3 → 0.0.4
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.
- data/Gemfile +2 -0
- data/Gemfile.lock +4 -0
- data/Rakefile +2 -1
- data/lib/arel_rails2_hacks.rb +49 -0
- data/lib/data_node.rb +4 -0
- data/lib/exceptions.rb +1 -0
- data/lib/mochigome.rb +1 -0
- data/lib/mochigome_ver.rb +1 -1
- data/lib/model_extensions.rb +202 -122
- data/lib/query.rb +295 -148
- data/test/app_root/app/models/category.rb +4 -2
- data/test/app_root/app/models/product.rb +14 -5
- data/test/app_root/app/models/sale.rb +3 -1
- data/test/app_root/config/initializers/arel.rb +2 -0
- data/test/app_root/db/migrate/20110817163830_create_tables.rb +0 -1
- data/test/console.sh +6 -0
- data/test/factories.rb +1 -2
- data/test/test_helper.rb +49 -49
- data/test/unit/data_node_test.rb +7 -0
- data/test/unit/model_extensions_test.rb +110 -93
- data/test/unit/query_test.rb +233 -64
- metadata +32 -14
data/test/test_helper.rb
CHANGED
@@ -1,67 +1,67 @@
|
|
1
1
|
ENV['RAILS_ENV'] = 'test'
|
2
2
|
|
3
|
-
prev_dir = Dir.getwd
|
4
3
|
begin
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
require "app_root/config/environment"
|
15
|
-
end
|
16
|
-
ensure
|
17
|
-
Dir.chdir(prev_dir)
|
4
|
+
# Used when running test files directly
|
5
|
+
d = File.expand_path File.dirname(__FILE__)
|
6
|
+
$LOAD_PATH << d
|
7
|
+
$LOAD_PATH << "#{d}/../lib"
|
8
|
+
$LOAD_PATH << "#{d}/app_root/config"
|
9
|
+
require "environment"
|
10
|
+
rescue LoadError
|
11
|
+
# This is needed for root-level rake task 'test'
|
12
|
+
require "app_root/config/environment"
|
18
13
|
end
|
19
14
|
|
20
15
|
require 'rubygems'
|
21
|
-
require 'minitest/autorun'
|
22
|
-
require 'redgreen'
|
23
16
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
17
|
+
if ENV['NO_MINITEST']
|
18
|
+
ActiveRecord::Migration.verbose = false
|
19
|
+
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate") # Migrations in the test app
|
20
|
+
else
|
21
|
+
require 'minitest/autorun'
|
22
|
+
require 'redgreen'
|
23
|
+
|
24
|
+
module MiniTest
|
25
|
+
def self.filter_backtrace(backtrace)
|
26
|
+
backtrace = backtrace.select do |e|
|
27
|
+
if ENV['FULL_BACKTRACE']
|
28
|
+
true
|
29
|
+
else
|
30
|
+
!(e.include?("/ruby/") || e.include?("/gems/"))
|
31
|
+
end
|
31
32
|
end
|
32
|
-
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
common_prefix = nil
|
35
|
+
backtrace.each do |elem|
|
36
|
+
next if elem.start_with? "./"
|
37
|
+
if common_prefix
|
38
|
+
until elem.start_with? common_prefix
|
39
|
+
common_prefix.chop!
|
40
|
+
end
|
41
|
+
else
|
42
|
+
common_prefix = String.new(elem)
|
40
43
|
end
|
41
|
-
else
|
42
|
-
common_prefix = String.new(elem)
|
43
44
|
end
|
44
|
-
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
46
|
+
return backtrace.map do |element|
|
47
|
+
if element.start_with? common_prefix && common_prefix.size < element.size
|
48
|
+
element[common_prefix.size, element.size]
|
49
|
+
elsif element.start_with? "./"
|
50
|
+
element[2, element.size]
|
51
|
+
elsif element.start_with?(Dir.getwd)
|
52
|
+
element[Dir.getwd.size+1, element.size]
|
53
|
+
else
|
54
|
+
element
|
55
|
+
end
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
58
|
-
end
|
59
|
-
|
60
59
|
|
61
|
-
require 'factories'
|
62
|
-
MiniTest::Unit::TestCase.send(:include, Factory::Syntax::Methods)
|
60
|
+
require 'factories'
|
61
|
+
MiniTest::Unit::TestCase.send(:include, Factory::Syntax::Methods)
|
63
62
|
|
64
|
-
MiniTest::Unit::TestCase.add_setup_hook do
|
65
|
-
|
66
|
-
|
63
|
+
MiniTest::Unit::TestCase.add_setup_hook do
|
64
|
+
ActiveRecord::Migration.verbose = false
|
65
|
+
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate") # Migrations in the test app
|
66
|
+
end
|
67
67
|
end
|
data/test/unit/data_node_test.rb
CHANGED
@@ -77,6 +77,13 @@ describe Mochigome::DataNode do
|
|
77
77
|
]
|
78
78
|
assert_equal @datanode.children.drop(1), new_children
|
79
79
|
end
|
80
|
+
|
81
|
+
it "understands forward-slash to mean indexing in children" do
|
82
|
+
@datanode << Mochigome::DataNode.new(:subdata, :alice)
|
83
|
+
@datanode << Mochigome::DataNode.new(:subdata, :bob)
|
84
|
+
assert_equal @datanode.children[0], @datanode/0
|
85
|
+
assert_equal @datanode.children[1], @datanode/1
|
86
|
+
end
|
80
87
|
end
|
81
88
|
|
82
89
|
describe "when populated" do
|
@@ -1,31 +1,5 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
2
|
|
3
|
-
describe "an input string" do
|
4
|
-
it "will be converted by auto_numerify to an integer if appropriate" do
|
5
|
-
[35, -35, 0].each do |n|
|
6
|
-
result = Mochigome::ReportFocus.auto_numerify(n.to_s)
|
7
|
-
assert_equal n, result
|
8
|
-
assert_kind_of Integer, result
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
it "will be converted by auto_numerify to a float if appropriate" do
|
13
|
-
[-35.5, 35.5, 0.0].each do |n|
|
14
|
-
result = Mochigome::ReportFocus.auto_numerify(n.to_s)
|
15
|
-
assert_in_delta n, result
|
16
|
-
assert_kind_of Float, result
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
it "will remain a string if it is not numeric" do
|
21
|
-
["", "zero", "yeehah", "foo0.0" "9.2bar"].each do |s|
|
22
|
-
result = Mochigome::ReportFocus.auto_numerify(s)
|
23
|
-
assert_equal s, result
|
24
|
-
assert_kind_of String, result
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
3
|
describe "an ActiveRecord model" do
|
30
4
|
before do
|
31
5
|
@model_class = Class.new(ActiveRecord::Base)
|
@@ -147,6 +121,27 @@ describe "an ActiveRecord model" do
|
|
147
121
|
assert_equal "Moby Dick", i.mochigome_focus.name
|
148
122
|
end
|
149
123
|
|
124
|
+
it "uses the primary key as the default ordering" do
|
125
|
+
@model_class.class_eval do
|
126
|
+
acts_as_mochigome_focus do |f|
|
127
|
+
f.name :last_name
|
128
|
+
end
|
129
|
+
end
|
130
|
+
assert_equal "id",
|
131
|
+
@model_class.mochigome_focus_settings.get_ordering
|
132
|
+
end
|
133
|
+
|
134
|
+
it "can specify a custom ordering" do
|
135
|
+
@model_class.class_eval do
|
136
|
+
acts_as_mochigome_focus do |f|
|
137
|
+
f.name :last_name
|
138
|
+
f.ordering :first_name
|
139
|
+
end
|
140
|
+
end
|
141
|
+
assert_equal "first_name",
|
142
|
+
@model_class.mochigome_focus_settings.get_ordering
|
143
|
+
end
|
144
|
+
|
150
145
|
it "can specify fields" do
|
151
146
|
@model_class.class_eval do
|
152
147
|
acts_as_mochigome_focus do |f|
|
@@ -275,119 +270,141 @@ describe "an ActiveRecord model" do
|
|
275
270
|
end
|
276
271
|
end
|
277
272
|
|
278
|
-
|
279
|
-
assert !Mochigome.reportFocusModels.include?(@model_class)
|
280
|
-
@model_class.class_eval do
|
281
|
-
acts_as_mochigome_focus
|
282
|
-
end
|
283
|
-
assert Mochigome.reportFocusModels.include?(@model_class)
|
284
|
-
end
|
273
|
+
# Actual use of aggregations is tested in query_test.
|
285
274
|
|
286
275
|
it "can specify aggregated data to be collected" do
|
287
276
|
@model_class.class_eval do
|
288
|
-
has_mochigome_aggregations
|
277
|
+
has_mochigome_aggregations do |a|
|
278
|
+
a.fields [
|
279
|
+
:average_x,
|
280
|
+
:Count,
|
281
|
+
{"bloo" => :sum_x}
|
282
|
+
]
|
283
|
+
end
|
289
284
|
end
|
290
|
-
# Peeking in past API to make sure it set the expressions correctly
|
291
285
|
assert_equal [
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
], @model_class.
|
286
|
+
"Whales average x",
|
287
|
+
"Whales Count",
|
288
|
+
"bloo"
|
289
|
+
], @model_class.mochigome_aggregation_settings.options[:fields].map{|a| a[:name]}
|
296
290
|
end
|
297
291
|
|
298
292
|
it "can specify aggregations with custom names" do
|
299
293
|
@model_class.class_eval do
|
300
|
-
has_mochigome_aggregations
|
294
|
+
has_mochigome_aggregations do |a|
|
295
|
+
a.fields [{"Mean X" => "avg x"}]
|
296
|
+
end
|
301
297
|
end
|
302
298
|
assert_equal [
|
303
|
-
|
304
|
-
], @model_class.
|
299
|
+
"Mean X"
|
300
|
+
], @model_class.mochigome_aggregation_settings.options[:fields].map{|a| a[:name]}
|
305
301
|
end
|
306
302
|
|
307
|
-
it "can specify aggregations with custom
|
303
|
+
it "can specify aggregations with custom arel expressions for value" do
|
308
304
|
@model_class.class_eval do
|
309
|
-
has_mochigome_aggregations
|
305
|
+
has_mochigome_aggregations do |a|
|
306
|
+
a.fields [{"The Answer" => [:sum, lambda{|t| t[:some_number_column]*2}]}]
|
307
|
+
end
|
310
308
|
end
|
311
309
|
assert_equal [
|
312
|
-
|
313
|
-
], @model_class.
|
310
|
+
"The Answer"
|
311
|
+
], @model_class.mochigome_aggregation_settings.options[:fields].map{|a| a[:name]}
|
314
312
|
end
|
315
313
|
|
316
|
-
it "can specify aggregations with custom
|
314
|
+
it "can specify aggregations with custom arel expressions for aggregation" do
|
317
315
|
@model_class.class_eval do
|
318
|
-
has_mochigome_aggregations
|
316
|
+
has_mochigome_aggregations do |a|
|
317
|
+
a.fields [{"The Answer" => [lambda{|a| a.sum}, :some_col]}]
|
318
|
+
end
|
319
319
|
end
|
320
320
|
assert_equal [
|
321
|
-
|
322
|
-
], @model_class.
|
321
|
+
"The Answer"
|
322
|
+
], @model_class.mochigome_aggregation_settings.options[:fields].map{|a| a[:name]}
|
323
323
|
end
|
324
324
|
|
325
|
-
it "cannot call
|
325
|
+
it "cannot call aggregation fields method with nonsense" do
|
326
326
|
assert_raises Mochigome::ModelSetupError do
|
327
327
|
@model_class.class_eval do
|
328
|
-
has_mochigome_aggregations
|
328
|
+
has_mochigome_aggregations do |a|
|
329
|
+
a.fields 3
|
330
|
+
end
|
329
331
|
end
|
330
332
|
end
|
331
333
|
assert_raises Mochigome::ModelSetupError do
|
332
334
|
@model_class.class_eval do
|
333
|
-
has_mochigome_aggregations
|
335
|
+
has_mochigome_aggregations do |a|
|
336
|
+
a.fields [42]
|
337
|
+
end
|
334
338
|
end
|
335
339
|
end
|
336
340
|
end
|
337
341
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
@product_a = create(:product, :name => "Product A", :price => 30)
|
343
|
-
@product_b = create(:product, :name => "Product B", :price => 50)
|
344
|
-
@sp1A = create(:store_product, :store => @store1, :product => @product_a)
|
345
|
-
@sp1B = create(:store_product, :store => @store1, :product => @product_b)
|
346
|
-
@sp2A = create(:store_product, :store => @store2, :product => @product_a)
|
347
|
-
@sp2B = create(:store_product, :store => @store2, :product => @product_b)
|
348
|
-
[
|
349
|
-
[2, @sp1A],
|
350
|
-
[4, @sp1B],
|
351
|
-
[7, @sp2A],
|
352
|
-
[3, @sp2B]
|
353
|
-
].each do |num, sp|
|
354
|
-
num.times { create(:sale, :store_product => sp) }
|
342
|
+
it "can specify hidden aggregation fields" do
|
343
|
+
@model_class.class_eval do
|
344
|
+
has_mochigome_aggregations do |a|
|
345
|
+
a.hidden_fields [:count]
|
355
346
|
end
|
356
347
|
end
|
348
|
+
agg = @model_class.mochigome_aggregation_settings.options[:fields].first
|
349
|
+
assert_equal "Whales count", agg[:name]
|
350
|
+
assert agg[:hidden]
|
351
|
+
end
|
357
352
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
353
|
+
it "can specify aggregation fields in ruby which post-process regular fields" do
|
354
|
+
@model_class.class_eval do
|
355
|
+
has_mochigome_aggregations do |a|
|
356
|
+
a.hidden_fields [:count]
|
357
|
+
a.fields_in_ruby [
|
358
|
+
{"Double count" => lambda{|row| row["Whales count"]*2}}
|
359
|
+
]
|
360
|
+
end
|
365
361
|
end
|
362
|
+
agg = @model_class.mochigome_aggregation_settings.options[:fields].last
|
363
|
+
assert_equal "Double count", agg[:name]
|
364
|
+
assert agg[:in_ruby]
|
365
|
+
end
|
366
366
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
367
|
+
def assoc_query_words_match(assoc, words)
|
368
|
+
q = assoc.call(Arel::Table.new(:foo).project(Arel.star)).to_sql
|
369
|
+
cur_word = words.shift
|
370
|
+
q.split(/[ .]/).each do |s_word|
|
371
|
+
if s_word.gsub(/["'`]+/, '').downcase == cur_word.downcase
|
372
|
+
cur_word = words.shift
|
373
|
+
end
|
371
374
|
end
|
375
|
+
return true if cur_word.nil?
|
376
|
+
raise "AQWM '#{q}': NO WORD MATCH ON '#{cur_word}'"
|
377
|
+
end
|
372
378
|
|
373
|
-
|
374
|
-
|
375
|
-
|
379
|
+
it "can convert a belongs_to association into a lambda that processes an arel relation" do
|
380
|
+
@model_class.class_eval do
|
381
|
+
belongs_to :store
|
376
382
|
end
|
383
|
+
assert assoc_query_words_match @model_class.arelified_assoc(:store),
|
384
|
+
%w{select * from foo join stores on fake store_id = stores id}
|
385
|
+
end
|
377
386
|
|
378
|
-
|
379
|
-
|
380
|
-
|
387
|
+
it "can convert a has_many association into an arel relation lambda" do
|
388
|
+
@model_class.class_eval do
|
389
|
+
has_many :stores
|
381
390
|
end
|
391
|
+
assert assoc_query_words_match @model_class.arelified_assoc(:stores),
|
392
|
+
%w{select * from foo join stores on fake id = stores whale_id}
|
393
|
+
end
|
382
394
|
|
383
|
-
|
384
|
-
|
385
|
-
|
395
|
+
it "can convert a has_one association into an arel relation lambda" do
|
396
|
+
@model_class.class_eval do
|
397
|
+
has_one :store
|
386
398
|
end
|
399
|
+
assert assoc_query_words_match @model_class.arelified_assoc(:store),
|
400
|
+
%w{select * from foo join stores on fake id = stores whale_id}
|
401
|
+
end
|
387
402
|
|
388
|
-
|
389
|
-
|
390
|
-
|
403
|
+
it "raises AssociationError on attempting to arelify a non-extant assoc" do
|
404
|
+
assert_raises Mochigome::AssociationError do
|
405
|
+
Store.arelified_assoc(:dinosaurs)
|
391
406
|
end
|
392
407
|
end
|
408
|
+
|
409
|
+
# TODO: Test proper conditions used on polymorphic assocs
|
393
410
|
end
|