google_data_source 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +25 -0
  6. data/Rakefile +31 -0
  7. data/google_data_source.gemspec +32 -0
  8. data/lib/assets/images/google_data_source/chart_bar_add.png +0 -0
  9. data/lib/assets/images/google_data_source/chart_bar_delete.png +0 -0
  10. data/lib/assets/images/google_data_source/loader.gif +0 -0
  11. data/lib/assets/javascripts/google_data_source/data_source_init.js +3 -0
  12. data/lib/assets/javascripts/google_data_source/extended_data_table.js +76 -0
  13. data/lib/assets/javascripts/google_data_source/filter_form.js +180 -0
  14. data/lib/assets/javascripts/google_data_source/google_visualization/combo_table.js.erb +113 -0
  15. data/lib/assets/javascripts/google_data_source/google_visualization/table.js +116 -0
  16. data/lib/assets/javascripts/google_data_source/google_visualization/timeline.js +13 -0
  17. data/lib/assets/javascripts/google_data_source/google_visualization/visualization.js.erb +141 -0
  18. data/lib/assets/javascripts/google_data_source/index.js +7 -0
  19. data/lib/dummy_engine.rb +5 -0
  20. data/lib/google_data_source.rb +33 -0
  21. data/lib/google_data_source/base.rb +281 -0
  22. data/lib/google_data_source/column.rb +31 -0
  23. data/lib/google_data_source/csv_data.rb +23 -0
  24. data/lib/google_data_source/data_date.rb +17 -0
  25. data/lib/google_data_source/data_date_time.rb +17 -0
  26. data/lib/google_data_source/helper.rb +69 -0
  27. data/lib/google_data_source/html_data.rb +6 -0
  28. data/lib/google_data_source/invalid_data.rb +14 -0
  29. data/lib/google_data_source/json_data.rb +78 -0
  30. data/lib/google_data_source/railtie.rb +36 -0
  31. data/lib/google_data_source/sql/models.rb +266 -0
  32. data/lib/google_data_source/sql/parser.rb +239 -0
  33. data/lib/google_data_source/sql_parser.rb +82 -0
  34. data/lib/google_data_source/template_handler.rb +31 -0
  35. data/lib/google_data_source/test_helper.rb +26 -0
  36. data/lib/google_data_source/version.rb +3 -0
  37. data/lib/google_data_source/xml_data.rb +25 -0
  38. data/lib/locale/de.yml +5 -0
  39. data/lib/reporting/action_controller_extension.rb +19 -0
  40. data/lib/reporting/grouped_set.rb +58 -0
  41. data/lib/reporting/helper.rb +110 -0
  42. data/lib/reporting/reporting.rb +352 -0
  43. data/lib/reporting/reporting_adapter.rb +27 -0
  44. data/lib/reporting/reporting_entry.rb +147 -0
  45. data/lib/reporting/sql_reporting.rb +220 -0
  46. data/test/lib/empty_reporting.rb +2 -0
  47. data/test/lib/test_reporting.rb +33 -0
  48. data/test/lib/test_reporting_b.rb +9 -0
  49. data/test/lib/test_reporting_c.rb +3 -0
  50. data/test/locales/en.models.yml +6 -0
  51. data/test/locales/en.reportings.yml +5 -0
  52. data/test/rails/reporting_renderer_test.rb +47 -0
  53. data/test/test_helper.rb +50 -0
  54. data/test/units/base_test.rb +340 -0
  55. data/test/units/csv_data_test.rb +36 -0
  56. data/test/units/grouped_set_test.rb +60 -0
  57. data/test/units/json_data_test.rb +68 -0
  58. data/test/units/reporting_adapter_test.rb +20 -0
  59. data/test/units/reporting_entry_test.rb +149 -0
  60. data/test/units/reporting_test.rb +374 -0
  61. data/test/units/sql_parser_test.rb +111 -0
  62. data/test/units/sql_reporting_test.rb +307 -0
  63. data/test/units/xml_data_test.rb +32 -0
  64. metadata +286 -0
@@ -0,0 +1,36 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+
3
+ class CvsDataTest < ActiveSupport::TestCase
4
+ def setup
5
+ setup_db
6
+ end
7
+
8
+ def teardown
9
+ teardown_db
10
+ end
11
+
12
+ test "csv rendering" do
13
+ items = [Item.create(:name => "Item Name", :description => "description", :number => 0)]
14
+ ds = GoogleDataSource::DataSource::Base.from_params({:tqx => "reqId:0;out:csv"})
15
+
16
+ columns = [
17
+ {:id => 'name', :label => 'Name', :type => 'string'},
18
+ {:id => 'number', :label => 'Number', :type => 'number'},
19
+ ]
20
+ ds.set(items, columns)
21
+
22
+ result = CSV.parse(ds.response, :col_sep => ';')
23
+ assert_equal [["Name", "Number"], ["Item Name", "0"]], result
24
+ end
25
+
26
+ test "number formating" do
27
+ items = [{:int => 1000000, :float => 1000000000.1}]
28
+ ds = GoogleDataSource::DataSource::Base.from_params({:tqx => "reqId:0;out:csv"})
29
+ columns = [
30
+ {:id => 'int', :type => 'number'},
31
+ {:id => 'float', :type => 'number'},
32
+ ]
33
+ ds.set(items, columns)
34
+ #TODO
35
+ end
36
+ end
@@ -0,0 +1,60 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+
3
+ class GroupedSetTest < ActiveSupport::TestCase
4
+ class TestReportingEntry < ReportingEntry
5
+ SUMMABLE_INT_FIELDS_REGEXP = /^(views|clicks|backfill_views|.*transaction_count)$/
6
+ SUMMABLE_FLOAT_FIELDS_REGEXP = /^(.*_sum|.*_sum_after_share)$/
7
+ NOT_SUMMABLE_FIELDS = %w(public_website_name)
8
+ end
9
+
10
+ setup do
11
+ entries = []
12
+ entries << TestReportingEntry.new({:date => Date.today.to_s(:db), :unit_id => "1", :ad_id => "1", :open_sum => "1" })
13
+ entries << TestReportingEntry.new({:date => Date.today.to_s(:db), :unit_id => "2", :ad_id => "1", :open_sum => "2" })
14
+ entries << TestReportingEntry.new({:date => Date.today.to_s(:db), :unit_id => "1", :ad_id => "2", :open_sum => "4" })
15
+ entries << TestReportingEntry.new({:date => Date.today.to_s(:db), :unit_id => "2", :ad_id => "2", :open_sum => "8" })
16
+ entries << TestReportingEntry.new({:date => 1.day.ago.to_s(:db), :unit_id => "1", :ad_id => "1", :open_sum => "16" })
17
+
18
+ @set = GroupedSet.new(entries)
19
+ end
20
+
21
+ test "should group by single key" do
22
+ data = @set.regroup(:date)
23
+ assert_equal 2, data.size
24
+ todays_entry = data.select { |e| e.date == Date.today.to_s(:db) }.first
25
+ assert_equal 15.0, todays_entry.open_sum
26
+ end
27
+
28
+ test "should group by multiple keys" do
29
+ data = @set.regroup(:date, :ad_id)
30
+ assert_equal 3, data.size
31
+ todays_ad1_entry = data.select { |e| e.date == Date.today.to_s(:db) && e.ad_id == "1"}.first
32
+ assert_equal 3.0, todays_ad1_entry.open_sum
33
+ end
34
+
35
+ test "should group by block" do
36
+ data = @set.regroup { |e| e.date }
37
+ assert_equal 2, data.size
38
+ todays_entry = data.select { |e| e.date == Date.today.to_s(:db) }.first
39
+ assert_equal 15.0, todays_entry.open_sum
40
+ end
41
+
42
+ test "should collapse" do
43
+ data = @set.collapse
44
+ assert_equal 31.0, data.open_sum
45
+ end
46
+
47
+ test "something interesting" do
48
+ result = [
49
+ {"campaign_name"=>"Campaign #5", "billing_subject"=>'foo', "campaign_id"=>"8188", "transaction_count"=>"2", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"45.3000001907349", "status"=>"confirmed", "pricing_association_id"=>"330"},
50
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>'blah', "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"1.10000002384186", "status"=>"confirmed", "pricing_association_id"=>"330"},
51
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>'blah', "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"1.10000002384186", "status"=>"open", "pricing_association_id"=>"330"},
52
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>"test", "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"31337", "status"=>"open", "pricing_association_id"=>"330"}
53
+ ]
54
+ result.collect!{ |re| TestReportingEntry.new(re) }
55
+ result = GroupedSet.new(result)
56
+ result = result.regroup('campaign_id', 'billing_subject')
57
+ assert_equal %w(foo blah test), result.collect(&:billing_subject)
58
+ assert_equal 3, result.size
59
+ end
60
+ end
@@ -0,0 +1,68 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+
3
+ class JsonDataTest < ActiveSupport::TestCase
4
+ context 'The JSON Data output engine' do
5
+ setup do
6
+ setup_db
7
+ end
8
+
9
+ teardown do
10
+ teardown_db
11
+ end
12
+
13
+ setup do
14
+ @items = [
15
+ Item.create(
16
+ :name => "Item Name",
17
+ :description => "description",
18
+ :number => 0,
19
+ :date => '2011-01-01',
20
+ :datetime => '2011-01-01 23:59:59'
21
+ ),
22
+ Item.create(
23
+ :name => "Item Name",
24
+ :description => "description",
25
+ :number => 10,
26
+ :date => nil,
27
+ :datetime => nil
28
+ )
29
+ ]
30
+ @datasource = GoogleDataSource::DataSource::Base.from_params({:tqx => "reqId:0;out:json"})
31
+
32
+ @columns = [
33
+ {:id => 'name', :label => 'Name', :type => 'string'},
34
+ {:id => 'number', :label => 'Number', :type => 'number'},
35
+ {:id => 'date', :label => 'Date', :type => 'date'},
36
+ {:id => 'datetime', :label => 'Datetime', :type => 'datetime'},
37
+ ]
38
+ @datasource.set(@items, @columns)
39
+ @raw_json = @datasource.response.match(/\{(.*)\}/)[0].gsub("\\\\", "\\");
40
+ @json = JSON.parse(@raw_json)
41
+ end
42
+
43
+ should "correctly render a json record" do
44
+ assert_equal 'Item Name', @json['table']['rows'].first.first.last[0]['v']
45
+ end
46
+
47
+ should "render an integer" do
48
+ assert_equal 0, @json['table']['rows'].first.first.last[1]['v']
49
+ assert_equal 10, @json['table']['rows'].last.first.last[1]['v']
50
+ end
51
+
52
+ should "correctly render a datetime value" do
53
+ assert_equal 'Date(2011, 0, 1, 23, 59, 59)', @json['table']['rows'].first.first.last[3]['v']
54
+ end
55
+
56
+ should "corretly render an empty datetime" do
57
+ assert_equal nil, @json['table']['rows'].last.first.last[3]['v']
58
+ end
59
+
60
+ should "correctly render a date value" do
61
+ assert_equal 'Date(2011, 0, 1)', @json['table']['rows'].first.first.last[2]['v']
62
+ end
63
+
64
+ should "corretly render an empty date" do
65
+ assert_equal nil, @json['table']['rows'].last.first.last[3]['v']
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,20 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+
3
+ class ReportingAdapterTest < ActiveSupport::TestCase
4
+ setup do
5
+ @adapter = ActiveRecord::ConnectionAdapters::ReportingAdapter.new
6
+ end
7
+
8
+ test "quote a single quote with a backslash" do
9
+ assert_equal "'foo\\''", @adapter.quote('foo\'')
10
+ end
11
+
12
+ test "return integers as quoted boolean" do
13
+ assert_equal "'1'", @adapter.quoted_true
14
+ assert_equal "'0'", @adapter.quoted_false
15
+ end
16
+
17
+ test "quote a backslash with a backslash" do
18
+ assert_equal "'foo\\\\'", @adapter.quote("foo\\")
19
+ end
20
+ end
@@ -0,0 +1,149 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+
3
+ class ReportingEntryTest < ActiveSupport::TestCase
4
+ class TestReportingEntry < ReportingEntry
5
+ SUMMABLE_INT_FIELDS_REGEXP = /int|another_value/
6
+ SUMMABLE_FLOAT_FIELDS_REGEXP = /float/
7
+ NOT_SUMMABLE_FIELDS = %w(hide_me)
8
+
9
+ def reduced
10
+ int - 1
11
+ end
12
+
13
+ def another_value
14
+ return 1
15
+ end
16
+
17
+ def hide_me
18
+ "I'm not shown in sum entry"
19
+ end
20
+
21
+ def show_me
22
+ "I'm shown!"
23
+ end
24
+ end
25
+
26
+ def setup
27
+ end
28
+
29
+ test "OpenStrcut like access" do
30
+ entry = TestReportingEntry.new(:foo => 'bar')
31
+ assert_equal 'bar', entry.foo
32
+ end
33
+
34
+ test "NoMethodError when attribute is not set" do
35
+ entry = TestReportingEntry.new()
36
+ assert_raise NoMethodError do
37
+ entry.foo
38
+ end
39
+ end
40
+
41
+ test "int values casting" do
42
+ entry = TestReportingEntry.new(:int => "1")
43
+ assert_equal 1, entry.int
44
+ assert entry.int.is_a?(Integer)
45
+ end
46
+
47
+ test "float values casting" do
48
+ entry = TestReportingEntry.new(:float => "1.5")
49
+ assert_equal 1.5, entry.float
50
+ assert entry.float.is_a?(Float)
51
+ end
52
+
53
+ test "accessors for numeric values should return 0 if the values are not defined" do
54
+ entry = TestReportingEntry.new
55
+ assert_equal 0, entry.int
56
+ assert_equal 0.0, entry.float
57
+ end
58
+
59
+ test "the + method should add up all numeric values while merging all others" do
60
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
61
+ entry2 = TestReportingEntry.new(:int => 2, :float => 2.5, :foo => 'bar')
62
+ sum = entry1 + entry2
63
+
64
+ assert_equal 3, sum.int
65
+ assert_equal 4.0, sum.float
66
+ assert_equal 'bar', sum.foo
67
+ end
68
+
69
+ test "the composite entry should also support a working + method" do
70
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
71
+ entry2 = TestReportingEntry.new(:int => 2, :float => 2.5, :foo => 'bar')
72
+ entry3 = TestReportingEntry.new(:int => 3, :float => 3.5, :foo => 'bar')
73
+ sum = entry1 + entry2 + entry3
74
+
75
+ assert_equal 6, sum.int
76
+ assert_equal 7.5, sum.float
77
+ end
78
+
79
+ test "virtual attributes should also work an composite entries" do
80
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
81
+ entry2 = TestReportingEntry.new(:int => 2, :float => 2.5, :foo => 'bar')
82
+
83
+ assert_equal 0, entry1.reduced
84
+ sum = entry1 + entry2
85
+ assert_equal 2, sum.reduced
86
+ end
87
+
88
+ test "to_sum_entry should only set numerical values and strip all others" do
89
+ entry = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
90
+ sum_entry = entry.to_sum_entry
91
+
92
+ assert_nil sum_entry.foo
93
+ assert_equal 1, sum_entry.int
94
+ assert_equal 1.5, sum_entry.float
95
+ end
96
+
97
+ test "to_sum_entry on composite entries" do
98
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
99
+ entry2 = TestReportingEntry.new(:int => 2, :float => 2.5, :foo => 'bar')
100
+ sum = (entry1 + entry2).to_sum_entry
101
+
102
+ assert_nil sum.foo
103
+ assert_equal 3, sum.int
104
+ end
105
+
106
+ test 'to_sum_entry should not return any explicitely hidden ruby columns' do
107
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
108
+ entry2 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
109
+ assert_equal nil, (entry1 + entry2).to_sum_entry.hide_me
110
+ end
111
+
112
+ test 'to_sum_entry should return all ruby columns in sum entry by default' do
113
+ entry1 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
114
+ entry2 = TestReportingEntry.new(:int => 1, :float => 1.5, :foo => 'bar')
115
+ assert_equal "I'm shown!", (entry1 + entry2).to_sum_entry.show_me
116
+ end
117
+
118
+ test "handle summable virtual attributes correclty" do
119
+ entry1 = TestReportingEntry.new()
120
+ entry2 = TestReportingEntry.new()
121
+ sum = entry1 + entry2
122
+
123
+ assert_equal 1, entry1.another_value
124
+ assert_equal 2, sum.another_value
125
+ end
126
+
127
+ test "be able to group by billing_subject" do
128
+ result = [
129
+ {"campaign_name"=>"Campaign #5", "billing_subject"=>'foo', "campaign_id"=>"8188", "transaction_count"=>"2", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"45.3000001907349", "status"=>"confirmed", "pricing_association_id"=>"330"},
130
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>'blah', "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"1.10000002384186", "status"=>"confirmed", "pricing_association_id"=>"330"},
131
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>'blah', "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"1.10000002384186", "status"=>"open", "pricing_association_id"=>"330"},
132
+ {"campaign_name"=>"Campaign #6", "billing_subject"=>"test", "campaign_id"=>"8189", "transaction_count"=>"1", "postview_transaction_count"=>"0", "click_transaction_count"=>"0", "sum"=>"31337", "status"=>"open", "pricing_association_id"=>"330"}
133
+ ]
134
+ result.collect!{ |re| ReportingEntry.new(re) }
135
+ result = result.group_by { |entry| "#{entry.send(:billing_subject)}" }.values
136
+ assert_equal 3, result.size
137
+
138
+ result = result.collect do |entries|
139
+ # Uses the class of the first element to build the composite element
140
+ entries.first.class.composite(entries)
141
+ end
142
+
143
+ assert_equal %w(foo blah test), result.collect(&:billing_subject)
144
+ end
145
+
146
+ test "virtual attributes " do
147
+ assert true
148
+ end
149
+ end
@@ -0,0 +1,374 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../test_helper"
2
+ require "test_reporting"
3
+ require "test_reporting_b"
4
+ require "test_reporting_c"
5
+ require "empty_reporting"
6
+
7
+ class ReportingTest < ActiveSupport::TestCase
8
+ def setup
9
+ @reporting = TestReporting.new
10
+ end
11
+
12
+ test "should return the columns hash, activerecord like" do
13
+ columns_hash = TestReporting.columns_hash
14
+ assert columns_hash.kind_of?(Hash), 'should be a hash'
15
+ assert_equal 'name', columns_hash.keys.first
16
+ end
17
+
18
+ test 'should return all column names' do
19
+ columns = ["name", "age", "address", "fullname", "fullfullname", "circle_a", "circle_b", "from_date", "to_date", "in_foo"]
20
+ assert_equal columns, TestReporting.column_names
21
+ end
22
+
23
+ test "should be able to use the accessors as expected" do
24
+ @reporting.name = 'test'
25
+ assert_equal 'test', @reporting.name
26
+ end
27
+
28
+ test "should return a hash with all default values" do
29
+ defaults = TestReporting.column_defaults
30
+ assert_equal 'foobar', defaults['name'], 'has a default value for name'
31
+ assert_equal nil, defaults['age'], 'has no default value for age'
32
+ end
33
+
34
+ test "rows should call aggregate once and only once" do
35
+ @reporting.data
36
+ @reporting.data
37
+ assert_equal 1, @reporting.aggregate_calls
38
+ end
39
+
40
+ test "should return true if the object is filterable through given column" do
41
+ assert ! @reporting.filterable_by?(:age), 'should not be filterable by normal columns'
42
+ assert @reporting.filterable_by?(:name), 'should be filterable even if the filter was defined as column before'
43
+ assert @reporting.filterable_by?(:from_date), 'should be filterable'
44
+ assert @reporting.filterable_by?('from_date'), 'should be filterable even if a string is given'
45
+ end
46
+
47
+ test "should have a seperate hash for filters" do
48
+ assert_equal({ :type => :string, :default => 'foobar' },
49
+ @reporting.class.datasource_filters["name"],
50
+ 'should be filterable by name')
51
+ end
52
+
53
+ test "from_params should typecast a date correctly" do
54
+ query = "where name = 'test name' and from_date = '2010-01-01'"
55
+ r = TestReporting.from_params({:tq => query})
56
+ assert_equal "test name", r.name
57
+ assert_equal "2010-01-01".to_date, r.from_date
58
+ end
59
+
60
+ test "select default value" do
61
+ r = TestReporting.new
62
+ assert_equal %w(name age), r.select
63
+ end
64
+
65
+ test "group by default value" do
66
+ r = TestReporting.new
67
+ assert_equal %w(name), r.group_by
68
+ end
69
+
70
+ test "limit by default value" do
71
+ r = TestReporting.new
72
+ assert_equal 1000, r.limit
73
+
74
+ r = TestReporting.new(:limit => 5)
75
+ assert_equal 5, r.limit
76
+
77
+ r = TestReporting.new(:limit => "10")
78
+ assert_equal 10, r.limit
79
+ end
80
+
81
+ test "from_params should set select" do
82
+ query = "select name"
83
+ r = TestReporting.from_params({:tq => query})
84
+ assert_equal ['name'], r.select
85
+ end
86
+
87
+ test "select should explode a * column" do
88
+ query = "select *"
89
+ r = TestReporting.from_params({:tq => query})
90
+ r.add_virtual_column(:virtual)
91
+
92
+ select = %w(name age address fullname fullfullname circle_a circle_b virtual)
93
+ assert_equal select, r.select
94
+ end
95
+
96
+ test "from_parmas should set group_by" do
97
+ query = "select name group by name"
98
+ r = TestReporting.from_params({:tq => query})
99
+ assert_equal ['name'], r.group_by
100
+ end
101
+
102
+ test 'should return groupable columns' do
103
+ reporting = TestReporting.new
104
+ assert_equal [ :age, :address ], reporting.groupable_columns
105
+ end
106
+
107
+ test "from_params should also take regular get parameters (not the query) into account" do
108
+ params = HashWithIndifferentAccess.new({:tq => '', :test_reporting => {:name => 'John'}})
109
+ r = TestReporting.from_params(params, 'test_reporting')
110
+ assert_equal "John", r.name
111
+ end
112
+
113
+ test "from_params should also take regular get parameters into account (with custom param name)" do
114
+ params = HashWithIndifferentAccess.new({:tq => '', :reporting => {:name => 'John'}})
115
+ r = TestReporting.from_params(params, :reporting)
116
+ assert_equal "John", r.name
117
+ end
118
+
119
+ test "from_params_with_date_range" do
120
+ query = "where `date` >= '2010-01-01' and `date`<='2010-02-01'"
121
+ r = TestReporting.from_params({:tq => query})
122
+ assert_equal "2010-01-01".to_date, r.from_date
123
+ assert_equal "2010-02-01".to_date, r.to_date
124
+ end
125
+
126
+ test "setting datasource columns" do
127
+ assert_equal :string, @reporting.datasource_columns['name'][:type]
128
+ assert_equal :string, TestReporting.datasource_columns['name'][:type]
129
+ end
130
+
131
+ test "get column definitions with respect to custom column labels and select" do
132
+ columns = @reporting.columns
133
+ assert_equal 2, columns.size
134
+ assert_equal 'name', columns[0][:id]
135
+ assert_equal :string, columns[0][:type]
136
+ end
137
+
138
+ test "different subclasses can have different sets of columns" do
139
+ assert TestReporting.datasource_columns != TestReportingB.datasource_columns
140
+ end
141
+
142
+ test "different subclasses can have different default selects and groupings" do
143
+ a = TestReporting.new
144
+ b = TestReportingB.new
145
+ assert a.group_by != b.group_by
146
+ assert a.select != b.select
147
+ end
148
+
149
+ test "sublasses should inherit all columns" do
150
+ reporting = TestReportingB.new
151
+ assert_nothing_raised do
152
+ assert reporting.datasource_columns.include?('name')
153
+ assert reporting.datasource_columns.include?('name_b')
154
+ end
155
+ end
156
+
157
+ test "subclasses should inherit all filters" do
158
+ assert_equal 5, TestReportingB.datasource_filters.count
159
+ end
160
+
161
+ test "subclasses should inherit all defaults" do
162
+ reporting = TestReportingC.new
163
+ assert_nothing_raised do
164
+ assert_equal ["name", "age"], reporting.defaults[:select]
165
+ end
166
+ end
167
+
168
+ test "should set limit and offset in from_params" do
169
+ reporting = TestReporting.from_params(:tq => "limit 10 offset 5")
170
+ assert_equal 10, reporting.limit
171
+ assert_equal 5, reporting.offset
172
+ reporting = TestReporting.from_params("query" => { "limit" => "10", "offset" => "5" })
173
+ assert_equal 10, reporting.limit
174
+ assert_equal 5, reporting.offset
175
+ end
176
+
177
+ test "should set the order_by attribute in from_params" do
178
+ reporting = TestReporting.from_params(:tq => "order by name")
179
+ assert_equal ['name', :asc], reporting.order_by
180
+ end
181
+
182
+ test "should return nil for order by and offset if not set" do
183
+ reporting = TestReporting.new
184
+ assert_nil reporting.order_by
185
+ #assert_nil reporting.limit
186
+ assert_nil reporting.offset
187
+ end
188
+
189
+ test "parse in( ) statement and put in 'in_xxx' attribute" do
190
+ reporting = TestReporting.from_params(:tq => "where foo in (1, 2)")
191
+ assert_equal %w(1 2), reporting.in_foo
192
+ end
193
+
194
+ test "translation of column labels" do
195
+ assert_equal 'AgeReportings', @reporting.column_label(:age)
196
+ assert_equal 'AddressModels', @reporting.column_label(:address)
197
+ end
198
+
199
+ test "the rows method should accept a reqired_columns option" do
200
+ @reporting.select = %w(age)
201
+ @reporting.group_by = []
202
+ assert_equal %w(age), @reporting.required_columns
203
+ @reporting.data(:required_columns => %w(name))
204
+ assert_equal %w(age name), @reporting.required_columns
205
+ end
206
+
207
+ test "serialization and deserialization" do
208
+ @reporting.select = %w(age)
209
+ @reporting.group_by = %w(age)
210
+ @reporting.order_by = %w(name)
211
+ @reporting.limit = 10
212
+ @reporting.offset = 20
213
+ @reporting.name = "John"
214
+
215
+ serialized = @reporting.serialize
216
+ reporting = TestReporting.deserialize(serialized)
217
+
218
+ assert_equal @reporting.select, reporting.select
219
+ assert_equal @reporting.group_by, reporting.group_by
220
+ assert_equal @reporting.order_by, reporting.order_by
221
+ assert_equal @reporting.limit, reporting.limit
222
+ assert_equal @reporting.offset, reporting.offset
223
+ assert_equal @reporting.name, reporting.name
224
+ end
225
+
226
+ test "from_params should recognize a serialized object and deserialize it" do
227
+ @reporting.select = %w(age)
228
+ @reporting.group_by = %w(age)
229
+ @reporting.name = "John"
230
+ serialized = @reporting.serialize
231
+ reporting = TestReporting.from_params(@reporting.to_params)
232
+
233
+ assert_equal @reporting.select, reporting.select
234
+ assert_equal @reporting.group_by, reporting.group_by
235
+ assert_equal @reporting.name, reporting.name
236
+ end
237
+
238
+ test 'from_params should accept a hash without a module name' do
239
+ reporting = TestReporting.from_params('test_reporting' => { 'select' => %w(age) })
240
+ assert_equal %w(age), reporting.select
241
+ end
242
+
243
+ test 'from_params from param "query" should work as well' do
244
+ reporting = TestReporting.from_params('query' => { 'name' => 'bla' })
245
+ assert_equal 'bla', reporting.name
246
+ end
247
+
248
+ test 'accept a "page" param and calculate the offset' do
249
+ reporting = TestReporting.from_params('query' => { 'page' => '3' })
250
+ assert reporting.limit.is_a?(Integer)
251
+ assert reporting.offset.is_a?(Integer)
252
+ assert_equal reporting.limit * 2, reporting.offset
253
+ end
254
+
255
+ test 'to_params should export the hash with a module name by default' do
256
+ assert_equal %w(test_reporting), @reporting.to_params.keys
257
+ end
258
+
259
+ test "initialize should handle date string with grace" do
260
+ reporting = TestReporting.new(:from_date => '2010-01-01')
261
+ assert_equal Date.parse('2010-01-01'), reporting.from_date
262
+ end
263
+
264
+ test "account for the 'requires' option in column definition" do
265
+ reporting = TestReporting.new
266
+ reporting.select = %w(fullname)
267
+ reporting.group_by = []
268
+ result = reporting.required_columns
269
+ assert_equal 2, result.size
270
+ assert result.include?('name')
271
+ assert result.include?('fullname')
272
+ end
273
+
274
+ test "'requires' option in column definition should work recursivly" do
275
+ reporting = TestReporting.new
276
+ reporting.select = %w(fullfullname)
277
+ reporting.group_by = []
278
+ result = reporting.required_columns
279
+ assert_equal 3, result.size
280
+ assert result.include?('name')
281
+ assert result.include?('fullname')
282
+ assert result.include?('fullfullname')
283
+ end
284
+
285
+ test "recognize circle dependencies in column and throw exception" do
286
+ reporting = TestReporting.new
287
+ reporting.select = %w(circle_a)
288
+ reporting.group_by = []
289
+ assert_raise CircularDependencyException do
290
+ reporting.required_columns
291
+ end
292
+ end
293
+
294
+ test "required_columns should handle virtual columns with grace" do
295
+ reporting = TestReporting.new
296
+ reporting.select = %w(virtual_column name)
297
+ assert_equal %w(virtual_column name), reporting.required_columns
298
+ end
299
+
300
+ ################################
301
+ # Test ActiveRecord extension
302
+ ################################
303
+
304
+ test "class loads" do
305
+ assert_nothing_raised { Reporting }
306
+ end
307
+
308
+ test "can add columns" do
309
+ self.class.class_eval %q{
310
+ class CanAddColumns < Reporting
311
+ %w(foo bar test).each { |c| column c }
312
+ end
313
+ }
314
+ assert_equal 3, CanAddColumns.columns.size
315
+ end
316
+
317
+ test "type properly set" do
318
+ self.class.class_eval %q{
319
+ class TypeProperlySet < Reporting
320
+ %w(string text date datetime boolean).each do |type|
321
+ filter "a_#{type}".to_sym, :type => type.to_sym
322
+ end
323
+ end
324
+ }
325
+
326
+ assert TypeProperlySet.columns.size > 0, 'no filters added'
327
+
328
+ %w(string text date datetime boolean).each do |type|
329
+ assert_equal type, TypeProperlySet.columns_hash["a_#{type}"].sql_type
330
+ end
331
+ end
332
+
333
+ test "default properly set" do
334
+ self.class.class_eval %q{
335
+ class DefaultPropertlySet < Reporting
336
+ filter :bicycle, :default => 'batavus'
337
+ end
338
+ }
339
+ assert_equal 'batavus', DefaultPropertlySet.new.bicycle
340
+ end
341
+
342
+ test "columns are humanizable" do
343
+ self.class.class_eval %q{
344
+ class Humanizable < Reporting
345
+ filter :bicycle, :human_name => 'fiets'
346
+ end
347
+ }
348
+
349
+ assert_equal 'fiets', Humanizable.columns_hash['bicycle'].humanize
350
+ end
351
+
352
+ test "create gbang raises no exception on valid" do
353
+ self.class.class_eval %q{
354
+ class CreateBangSuccess < Reporting; end
355
+ }
356
+
357
+ assert_nothing_raised do
358
+ CreateBangSuccess.create!
359
+ end
360
+ end
361
+
362
+ test "create bang raises exception on invalid" do
363
+ self.class.class_eval %q{
364
+ class CreateBangFailure < Reporting
365
+ filter :required_field
366
+ validates_presence_of :required_field
367
+ end
368
+ }
369
+
370
+ assert_raises ActiveRecord::RecordInvalid do
371
+ CreateBangFailure.create!
372
+ end
373
+ end
374
+ end