google_data_source 0.7.6

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.
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,31 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class Column
4
+ attr_accessor :type, :id, :label, :pattern
5
+
6
+ COLTYPES = %w(boolean number string date datetime timeofday)
7
+ #COLKEYS = [:type, :id, :label, :pattern]
8
+
9
+ def initialize(params)
10
+ @type = (params[:type] || :string).to_s
11
+ @id = params[:id].to_sym
12
+ @label = params[:label]
13
+ @pattern = params[:pattern]
14
+ end
15
+
16
+ def valid?
17
+ COLTYPES.include?(type)
18
+ end
19
+
20
+ def to_h
21
+ {
22
+ :id => id,
23
+ :type => type,
24
+ :label => label,
25
+ :pattern => pattern
26
+ }
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ require 'csv'
2
+ #require 'action_pack'
3
+
4
+ module GoogleDataSource
5
+ module DataSource
6
+ class CsvData < Base
7
+ #include ActionView::Helpers::NumberHelper
8
+ def response
9
+ result = CSV.generate(:col_sep => ';') do |csv|
10
+ csv << columns.map { |col| col.label || col.id || col.type }
11
+ data.each do |datarow|
12
+ csv << datarow.map do |c|
13
+ c.is_a?(Hash) ? c[:v] : c
14
+ # TODO
15
+ #value.is_a?(Float) ? number_with_delimiter(value) : value
16
+ end
17
+ end
18
+ end
19
+ result.force_encoding 'UTF-8'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class DataDate
4
+ def initialize(date)
5
+ @date = date
6
+ end
7
+
8
+ def as_json(options=nil)
9
+ if @date
10
+ "Date(#{@date.year}, #{@date.month-1}, #{@date.day})"
11
+ else
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class DataDateTime
4
+ def initialize(datetime)
5
+ @datetime = datetime
6
+ end
7
+
8
+ def as_json(options=nil)
9
+ if @datetime
10
+ "Date(#{@datetime.year}, #{@datetime.month-1}, #{@datetime.day}, #{@datetime.hour}, #{@datetime.min}, #{@datetime.sec})"
11
+ else
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,69 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ module Helper
4
+ # Includes the JavaScript files neccessary for data source visualization
5
+ # The helper should be called the header of the layout
6
+ def google_data_source_includes(ssl = false)
7
+ html = "<script src='http#{"s" if ssl}://www.google.com/jsapi' type='text/javascript'></script>"
8
+ html.html_safe
9
+ end
10
+
11
+ # Shows a Google visualization.
12
+ # Available +types+ include:
13
+ # * Table
14
+ # * TimeLine
15
+ # +url+ defines the URL to the data source and defaults to +url_for(:format => 'datasource').
16
+ # The options are generally passed to the visualization JS objects after camlizing the keys
17
+ # Options that are not passed include:
18
+ # * +:container_id+ : The Dom id of the container element
19
+ def google_visualization(type, url = nil, options = {})
20
+ # extract options that are not meant for the javascript part
21
+ container_id = options.delete(:container_id) || "google_#{type.underscore}"
22
+
23
+ # camelize option keys
24
+ js_options = options.to_a.inject({}) { |memo, opt| memo[opt.first.to_s.camelize(:lower)] = opt.last; memo }
25
+
26
+ url ||= url_for(:format => 'datasource')
27
+ html = content_tag(:div, :id => container_id) { }
28
+ html << javascript_tag("DataSource.Visualization.create('#{type.camelize}', '#{url}', '#{container_id}', #{js_options.to_json});")
29
+
30
+ html << reporting_controls(container_id, options)
31
+ html
32
+ end
33
+
34
+ def reporting_controls(container_id, options)
35
+ html = tag(:div, {:id => "#{container_id}_controls", :class => "data_source_controls"}, true)
36
+
37
+ # Add Export links
38
+ unless options[:exportable_as].nil? || options[:exportable_as].empty?
39
+ html << tag(:div, {:id => "#{container_id}_export_as"}, true)
40
+ html << t('google_data_source.export_links.export_as')
41
+ html << ' '
42
+ options[:exportable_as].each do |format|
43
+ html << google_datasource_export_link(format)
44
+ end
45
+ html << ActiveSupport::SafeBuffer.new("</div>")
46
+ end
47
+
48
+ html << ActiveSupport::SafeBuffer.new("</div>") # ugly, any ideas?
49
+ html
50
+ end
51
+
52
+ # Returns a export link for the given format
53
+ def google_datasource_export_link(format)
54
+ label = t("google_data_source.export_links.#{format}")
55
+ link_to(label, '#', :class => "export_as_#{format}")
56
+ end
57
+
58
+ # Shows a Google data table
59
+ def google_datatable(url = nil, options = {})
60
+ google_visualization('Table', url, options)
61
+ end
62
+
63
+ # Shows a Google annotated timeline
64
+ def google_timeline(url = nil, options = {})
65
+ google_visualization('TimeLine', url, options)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,6 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class HtmlData < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class InvalidData < Base
4
+ def initialize(gviz_params)
5
+ super(gviz_params)
6
+ end
7
+
8
+ def validate
9
+ super
10
+ add_error(:out, "Invalid output format: #{@params[:out]}. Valid ones are json,csv,html")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,78 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ class JsonData < Base
4
+
5
+ def initialize(gdata_params)
6
+ super(gdata_params)
7
+ @responseHandler = "google.visualization.Query.setResponse"
8
+ end
9
+
10
+ # Returns the datasource in JSON format. It supports the +responseHandler+
11
+ # parameter. All the errors are returned with the +invalid_request+ key.
12
+ # Warnings are unsupported (yet).
13
+ def response
14
+ rsp = {}
15
+ rsp[:version] = @version
16
+ rsp[:reqId] = @params[:reqId] if @params.key?(:reqId)
17
+ if valid?
18
+ rsp[:status] = "ok"
19
+ rsp[:table] = datatable unless data.nil?
20
+ else
21
+ rsp[:status] = "error"
22
+ rsp[:errors] = @errors.values.collect do |error|
23
+ { :reason => "invalid_request" , :message => error }
24
+ end
25
+ end
26
+ "#{@params[:responseHandler] || @responseHandler}(#{rsp.to_json});#{callback}"
27
+ end
28
+
29
+ # Renders the part of the JSON response that contains the dataset.
30
+ def datatable
31
+ dt = {}
32
+ dt[:cols] = columns.collect(&:to_h)
33
+ dt[:rows] = []
34
+ data.each do |datarow|
35
+ row = []
36
+ datarow.each_with_index do |datacell, colnum|
37
+ if datacell.is_a?(Hash)
38
+ row << {
39
+ :v => convert_cell(datacell[:v], columns[colnum].type),
40
+ :f => datacell[:f]
41
+ }
42
+ else
43
+ row << { :v => convert_cell(datacell, columns[colnum].type) }
44
+ end
45
+ end
46
+
47
+ dt[:rows] << { :c => row }
48
+ end
49
+ return dt
50
+ end
51
+ protected :datatable
52
+
53
+ # Converts a value in the dataset into a format suitable for the
54
+ # column it belongs to.
55
+ #
56
+ # Datasets are expected to play nice, and try to adhere to the columns they
57
+ # intend to export as much as possible. This method doesn't do anything more
58
+ # than the very minimum to ensure a formally valid gviz export.
59
+ def convert_cell(value, coltype)
60
+ case coltype
61
+ when "boolean"
62
+ !!value
63
+ when "number"
64
+ value # TODO to_i ???
65
+ when "string"
66
+ value
67
+ when "date"
68
+ DataDate.new(value.is_a?(String) ? Date.parse(value) : value) rescue nil
69
+ when "datetime"
70
+ DataDateTime.new(value.is_a?(String) ? DateTime.parse(value) : value) rescue nil
71
+ when "timeofday"
72
+ [ value.hour, value.min, value.sec, value.usec / 1000 ]
73
+ end
74
+ end
75
+ protected :convert_cell
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ # all rails specific extensions
4
+ require 'reporting/action_controller_extension'
5
+ require 'reporting/helper'
6
+ require 'google_data_source/helper'
7
+ require 'google_data_source/test_helper'
8
+
9
+ module GoogleDataSource
10
+ class Railtie < Rails::Railtie
11
+ initializer 'google_data_source.initialize', :after => :after_initialize do
12
+ # register helper
13
+ ActionView::Base.send :include, GoogleDataSource::DataSource::Helper
14
+ ActionView::Base.send :include, GoogleDataSource::Reporting::Helper
15
+
16
+ # Register controller extension
17
+ #ActionController::Base.class_eval do
18
+ # include GoogleDataSource::Reporting::ActionControllerExtension
19
+ # alias_method_chain :render, :reporting
20
+ #end
21
+
22
+ # I18n
23
+ I18n.load_path.unshift *Dir[File.join(File.dirname(__FILE__), '..', 'locale', '*.{rb,yml}')]
24
+
25
+ # Register TemplateHandler
26
+ # TODO set mime type to CSV / HTML according to the output format
27
+ Mime::Type.register "application/json", :datasource
28
+
29
+ ActionView::Template.register_template_handler(:datasource,
30
+ GoogleDataSource::DataSource::TemplateHandler
31
+ )
32
+
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,266 @@
1
+ module GoogleDataSource
2
+ module DataSource
3
+ module Sql
4
+ class WithHelpers
5
+ class << self
6
+ include RParsec::DefHelper
7
+ end
8
+ end
9
+
10
+ class Expr < WithHelpers
11
+ def self.binary(*ops)
12
+ ops.each do |op|
13
+ define_method(op) do |other|
14
+ BinaryExpr.new(self, op, other)
15
+ end
16
+ end
17
+ end
18
+ binary :+,:-,:*,:/,:%
19
+ def -@
20
+ PrefixExpr.new(:-, self)
21
+ end
22
+ def self.compare(*ops)
23
+ ops.each do |op|
24
+ define_method(op) do |other|
25
+ ComparePredicate.new(self, op, other)
26
+ end
27
+ end
28
+ end
29
+ compare :'==', :'>', :'<', :'>=', :'<='
30
+ end
31
+
32
+ class LiteralExpr < Expr
33
+ def_readable :lit
34
+ def to_s
35
+ lit
36
+ end
37
+ end
38
+
39
+ class VarExpr < Expr
40
+ def_readable :name
41
+ def to_s
42
+ "$#{name}"
43
+ end
44
+ end
45
+
46
+ class WordExpr < Expr
47
+ def_readable :name
48
+ def to_s
49
+ name
50
+ end
51
+ end
52
+
53
+ class QualifiedColumnExpr < Expr
54
+ def_readable :owner, :col
55
+ def to_s
56
+ "#{owner}.#{col}"
57
+ end
58
+ end
59
+
60
+ class WildcardExpr < Expr
61
+ Instance = WildcardExpr.new
62
+ def to_s
63
+ '*'
64
+ end
65
+ end
66
+
67
+ class BinaryExpr < Expr
68
+ def_readable :left, :op, :right
69
+ def to_s
70
+ "(#{left} #{op} #{right})"
71
+ end
72
+ end
73
+
74
+ class PostfixExpr < Expr
75
+ def_readable :expr, :op
76
+ def to_s
77
+ "(#{expr} #{op})"
78
+ end
79
+ end
80
+
81
+ class PrefixExpr < Expr
82
+ def_readable :op, :expr
83
+ def to_s
84
+ "(#{op} #{expr})"
85
+ end
86
+ end
87
+
88
+ def cases_string cases, default, result
89
+ cases.each do |cond, val|
90
+ result << " when #{cond}: #{val}"
91
+ end
92
+ unless default.nil?
93
+ result << " else #{default}"
94
+ end
95
+ result << " end"
96
+ result
97
+ end
98
+
99
+ class SimpleCaseExpr < Expr
100
+ def_readable :expr, :cases, :default
101
+ def to_s
102
+ cases_string cases, default, "case #{expr}"
103
+ end
104
+ end
105
+
106
+ class CaseExpr < Expr
107
+ def_readable :cases, :default
108
+ def to_s
109
+ cases_string cases, default, 'case'
110
+ end
111
+ end
112
+
113
+
114
+ ############Predicate########################
115
+ class Predicate < WithHelpers
116
+ include RParsec::DefHelper
117
+ end
118
+
119
+ class ComparePredicate < Predicate
120
+ def_readable :left, :op, :right
121
+
122
+ def to_s
123
+ "#{left} #{op_name} #{right}"
124
+ end
125
+
126
+ def op_name
127
+ case op when :"!=" then "<>" else op.to_s end
128
+ end
129
+ end
130
+
131
+ class CompoundPredicate < Predicate
132
+ def_readable :left, :op, :right
133
+ def to_s
134
+ "(#{left} #{op} #{right})"
135
+ end
136
+ end
137
+
138
+ class NotPredicate < Predicate
139
+ def_readable :predicate
140
+ def to_s
141
+ "(not #{predicate})"
142
+ end
143
+ end
144
+
145
+ class ExistsPredicate < Predicate
146
+ def_readable :relation
147
+ def to_s
148
+ "exists(#{relation})"
149
+ end
150
+ end
151
+
152
+ class NotExistsPredicate < Predicate
153
+ def_readable :relation
154
+ def to_s
155
+ "not exists(#{relation})"
156
+ end
157
+ end
158
+
159
+ class InRelationPredicate < Predicate
160
+ def_readable :expr, :relation
161
+ def to_s
162
+ "#{expr} in (#{relation})"
163
+ end
164
+ end
165
+
166
+ class NotInRelationPredicate < Predicate
167
+ def_readable :expr, :relation
168
+ def to_s
169
+ "#{expr} not in (#{relation})"
170
+ end
171
+ end
172
+
173
+ class InPredicate < Predicate
174
+ def_readable :expr, :vals
175
+ def to_s
176
+ "#{expr} in (#{vals.join(', ')})"
177
+ end
178
+ end
179
+
180
+ class NotInPredicate < Predicate
181
+ def_readable :expr, :vals
182
+ def to_s
183
+ "#{expr} not in (#{vals.join(', ')})"
184
+ end
185
+ end
186
+
187
+ class BetweenPredicate < Predicate
188
+ def_readable :expr, :from, :to
189
+ def to_s
190
+ "#{expr} between #{from} and #{to}"
191
+ end
192
+ end
193
+
194
+ class NotBetweenPredicate < Predicate
195
+ def_readable :expr, :from, :to
196
+ def to_s
197
+ "#{expr} not between #{from} and #{to}"
198
+ end
199
+ end
200
+
201
+ class GroupComparisonPredicate < Predicate
202
+ def_readable :group1, :op, :group2
203
+ def to_s
204
+ "#{list_exprs group1} #{op} #{list_exprs group2}"
205
+ end
206
+ def list_exprs exprs
207
+ "(#{exprs.join(', ')})"
208
+ end
209
+ end
210
+ #############Relations######################
211
+
212
+ class OrderElement < WithHelpers
213
+ def_readable :expr, :asc
214
+ def to_s
215
+ result = "#{expr}"
216
+ unless asc
217
+ result << ' desc'
218
+ end
219
+ result
220
+ end
221
+ end
222
+
223
+ class GroupByClause < WithHelpers
224
+ def_readable :exprs, :having
225
+ def to_s
226
+ result = exprs.join(', ')
227
+ unless having.nil?
228
+ result << " having #{having}"
229
+ end
230
+ result
231
+ end
232
+ end
233
+
234
+ class Relation < WithHelpers
235
+ def as_inner
236
+ to_s
237
+ end
238
+ end
239
+
240
+ class SelectRelation < Relation
241
+ def_readable :select, :where, :groupby, :orderby, :limit, :offset
242
+
243
+ def to_s
244
+ result = "select"
245
+ result << " #{select.join(', ')}"
246
+ unless where.nil?
247
+ result << " where #{where}"
248
+ end
249
+ unless groupby.nil?
250
+ result << " group by #{groupby}"
251
+ end
252
+ unless orderby.nil?
253
+ result << " order by #{orderby.join(', ')}"
254
+ end
255
+ unless limit.nil?
256
+ result << " limit #{limit}"
257
+ end
258
+ unless offset.nil?
259
+ result << " offset #{offset}"
260
+ end
261
+ result
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end