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.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +25 -0
- data/Rakefile +31 -0
- data/google_data_source.gemspec +32 -0
- data/lib/assets/images/google_data_source/chart_bar_add.png +0 -0
- data/lib/assets/images/google_data_source/chart_bar_delete.png +0 -0
- data/lib/assets/images/google_data_source/loader.gif +0 -0
- data/lib/assets/javascripts/google_data_source/data_source_init.js +3 -0
- data/lib/assets/javascripts/google_data_source/extended_data_table.js +76 -0
- data/lib/assets/javascripts/google_data_source/filter_form.js +180 -0
- data/lib/assets/javascripts/google_data_source/google_visualization/combo_table.js.erb +113 -0
- data/lib/assets/javascripts/google_data_source/google_visualization/table.js +116 -0
- data/lib/assets/javascripts/google_data_source/google_visualization/timeline.js +13 -0
- data/lib/assets/javascripts/google_data_source/google_visualization/visualization.js.erb +141 -0
- data/lib/assets/javascripts/google_data_source/index.js +7 -0
- data/lib/dummy_engine.rb +5 -0
- data/lib/google_data_source.rb +33 -0
- data/lib/google_data_source/base.rb +281 -0
- data/lib/google_data_source/column.rb +31 -0
- data/lib/google_data_source/csv_data.rb +23 -0
- data/lib/google_data_source/data_date.rb +17 -0
- data/lib/google_data_source/data_date_time.rb +17 -0
- data/lib/google_data_source/helper.rb +69 -0
- data/lib/google_data_source/html_data.rb +6 -0
- data/lib/google_data_source/invalid_data.rb +14 -0
- data/lib/google_data_source/json_data.rb +78 -0
- data/lib/google_data_source/railtie.rb +36 -0
- data/lib/google_data_source/sql/models.rb +266 -0
- data/lib/google_data_source/sql/parser.rb +239 -0
- data/lib/google_data_source/sql_parser.rb +82 -0
- data/lib/google_data_source/template_handler.rb +31 -0
- data/lib/google_data_source/test_helper.rb +26 -0
- data/lib/google_data_source/version.rb +3 -0
- data/lib/google_data_source/xml_data.rb +25 -0
- data/lib/locale/de.yml +5 -0
- data/lib/reporting/action_controller_extension.rb +19 -0
- data/lib/reporting/grouped_set.rb +58 -0
- data/lib/reporting/helper.rb +110 -0
- data/lib/reporting/reporting.rb +352 -0
- data/lib/reporting/reporting_adapter.rb +27 -0
- data/lib/reporting/reporting_entry.rb +147 -0
- data/lib/reporting/sql_reporting.rb +220 -0
- data/test/lib/empty_reporting.rb +2 -0
- data/test/lib/test_reporting.rb +33 -0
- data/test/lib/test_reporting_b.rb +9 -0
- data/test/lib/test_reporting_c.rb +3 -0
- data/test/locales/en.models.yml +6 -0
- data/test/locales/en.reportings.yml +5 -0
- data/test/rails/reporting_renderer_test.rb +47 -0
- data/test/test_helper.rb +50 -0
- data/test/units/base_test.rb +340 -0
- data/test/units/csv_data_test.rb +36 -0
- data/test/units/grouped_set_test.rb +60 -0
- data/test/units/json_data_test.rb +68 -0
- data/test/units/reporting_adapter_test.rb +20 -0
- data/test/units/reporting_entry_test.rb +149 -0
- data/test/units/reporting_test.rb +374 -0
- data/test/units/sql_parser_test.rb +111 -0
- data/test/units/sql_reporting_test.rb +307 -0
- data/test/units/xml_data_test.rb +32 -0
- 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,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
|