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,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