standardapi 6.0.0.26 → 6.1.0
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.
- checksums.yaml +4 -4
- data/README.md +15 -6
- data/lib/standard_api.rb +5 -0
- data/lib/standard_api/access_control_list.rb +114 -0
- data/lib/standard_api/active_record/connection_adapters/postgresql/schema_statements.rb +21 -0
- data/lib/standard_api/controller.rb +75 -78
- data/lib/standard_api/errors.rb +9 -0
- data/lib/standard_api/helpers.rb +66 -13
- data/lib/standard_api/includes.rb +9 -0
- data/lib/standard_api/middleware/query_encoding.rb +3 -3
- data/lib/standard_api/railtie.rb +13 -2
- data/lib/standard_api/route_helpers.rb +5 -5
- data/lib/standard_api/test_case.rb +24 -14
- data/lib/standard_api/test_case/calculate_tests.rb +10 -4
- data/lib/standard_api/test_case/create_tests.rb +13 -15
- data/lib/standard_api/test_case/index_tests.rb +14 -4
- data/lib/standard_api/test_case/schema_tests.rb +25 -3
- data/lib/standard_api/test_case/show_tests.rb +1 -0
- data/lib/standard_api/test_case/update_tests.rb +8 -9
- data/lib/standard_api/version.rb +1 -1
- data/lib/standard_api/views/application/_record.json.jbuilder +33 -30
- data/lib/standard_api/views/application/_record.streamer +36 -34
- data/lib/standard_api/views/application/_schema.json.jbuilder +68 -0
- data/lib/standard_api/views/application/_schema.streamer +78 -0
- data/lib/standard_api/views/application/new.streamer +1 -1
- data/lib/standard_api/views/application/schema.json.jbuilder +1 -12
- data/lib/standard_api/views/application/schema.streamer +1 -16
- data/test/standard_api/caching_test.rb +43 -0
- data/test/standard_api/helpers_test.rb +172 -0
- data/test/standard_api/performance.rb +39 -0
- data/test/standard_api/route_helpers_test.rb +33 -0
- data/test/standard_api/standard_api_test.rb +699 -0
- data/test/standard_api/test_app.rb +1 -0
- data/test/standard_api/test_app/app/controllers/acl/account_acl.rb +15 -0
- data/test/standard_api/test_app/app/controllers/acl/property_acl.rb +27 -0
- data/test/standard_api/test_app/app/controllers/acl/reference_acl.rb +7 -0
- data/test/standard_api/test_app/controllers.rb +13 -45
- data/test/standard_api/test_app/models.rb +38 -4
- data/test/standard_api/test_app/test/factories.rb +4 -3
- data/test/standard_api/test_app/views/photos/_photo.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_photo.streamer +18 -0
- data/test/standard_api/test_app/views/photos/_schema.json.jbuilder +1 -0
- data/test/standard_api/test_app/views/photos/_schema.streamer +3 -0
- data/test/standard_api/test_app/views/photos/schema.json.jbuilder +1 -1
- data/test/standard_api/test_app/views/photos/schema.streamer +1 -0
- data/test/standard_api/test_helper.rb +238 -0
- metadata +33 -17
- data/test/standard_api/test_app/log/test.log +0 -129516
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7134ec77418da381ff4cb9aa8717f797784ab1f4d45faefde89261e6e7a82ad9
|
4
|
+
data.tar.gz: 83e924e7fd2ded8c5d4239f9d775ef232fa985a4daa838aec374105eed65df2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e477baa763d068d112a29a9539145b75edf47be726c21e679fc4842ae63157914c8b2753f0f7e338c43fe2e328cbe85c4cb3681958113b5533af478bcb43fc3
|
7
|
+
data.tar.gz: 551a57b70b62f0f6c27f97aba97930b220a40b80799058a7c1a35d0d4a0afa787dd760ac01d42110d050a4d0d0f1387d6b00d63629c523c96e0b49a6910b77c1
|
data/README.md
CHANGED
@@ -105,11 +105,12 @@ including the author, the photos that the author took can also be included.
|
|
105
105
|
# API Usage
|
106
106
|
Resources can be queried via REST style end points
|
107
107
|
```
|
108
|
-
GET /records/:id
|
109
|
-
PATCH /records/:id
|
110
|
-
GET /records
|
111
|
-
|
112
|
-
|
108
|
+
GET /records/:id fetch record
|
109
|
+
PATCH /records/:id update record
|
110
|
+
GET /records/ fetch records
|
111
|
+
GET /records/calculate apply count and other functions on record(s)
|
112
|
+
POST /records create record
|
113
|
+
DELETE /records destroy record
|
113
114
|
```
|
114
115
|
|
115
116
|
All resource end points can be filtered, ordered, limited, offset, and have includes. All options are passed via query string in a nested URI encoded format.
|
@@ -163,8 +164,16 @@ location: {within: 0106000020e6...} WHERE ST_Within("listings"."location
|
|
163
164
|
|
164
165
|
// On Relationships
|
165
166
|
property: {size: 10000} JOIN properties WHERE properties.size = 10000"
|
167
|
+
```
|
168
|
+
## Calculations
|
169
|
+
|
170
|
+
The only change on calculate routes is the `selects` paramater contains the functions to apply. Currently just `minimum`, `maximum`, `average`, `sum`, and `count`.
|
166
171
|
|
167
|
-
|
172
|
+
```
|
173
|
+
{ count: '*' } SELECT COUNT(*)
|
174
|
+
[{ count: '*' }] SELECT COUNT(*)
|
175
|
+
[{ count: '*', maximum: :id, minimum: :id }] SELECT COUNT(*), MAXIMUM(id), MINIMUM(id)
|
176
|
+
[{ maximum: :id }, { maximum: :count }] SELECT MAXIMUM(id), MAXIMUM(count)
|
168
177
|
```
|
169
178
|
|
170
179
|
# Testing
|
data/lib/standard_api.rb
CHANGED
@@ -14,4 +14,9 @@ require 'standard_api/includes'
|
|
14
14
|
require 'standard_api/controller'
|
15
15
|
require 'standard_api/helpers'
|
16
16
|
require 'standard_api/route_helpers'
|
17
|
+
require 'standard_api/active_record/connection_adapters/postgresql/schema_statements'
|
17
18
|
require 'standard_api/railtie'
|
19
|
+
|
20
|
+
module StandardAPI
|
21
|
+
autoload :AccessControlList, 'standard_api/access_control_list'
|
22
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module StandardAPI
|
2
|
+
module AccessControlList
|
3
|
+
|
4
|
+
def self.traverse(path, prefix: nil, &block)
|
5
|
+
path.children.each do |child|
|
6
|
+
if child.file? && child.basename('.rb').to_s.ends_with?('_acl')
|
7
|
+
block.call([prefix, child.basename('.rb').to_s].compact.join('/'))
|
8
|
+
elsif child.directory?
|
9
|
+
traverse(child, prefix: [prefix, child.basename.to_s].compact.join('/'), &block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(application_controller)
|
15
|
+
acl_dir = Rails.application.root.join('app', 'controllers', 'acl')
|
16
|
+
return if !acl_dir.exist?
|
17
|
+
|
18
|
+
traverse(acl_dir) do |child|
|
19
|
+
mod = child.classify.constantize
|
20
|
+
prefix = child.delete_suffix('_acl').gsub('/', '_')
|
21
|
+
|
22
|
+
[:orders, :includes, :attributes].each do |m|
|
23
|
+
next if !mod.instance_methods.include?(m)
|
24
|
+
mod.send :alias_method, "#{prefix}_#{m}".to_sym, m
|
25
|
+
mod.send :remove_method, m
|
26
|
+
end
|
27
|
+
|
28
|
+
if mod.instance_methods.include?(:nested)
|
29
|
+
mod.send :alias_method, "nested_#{prefix}_attributes".to_sym, :nested
|
30
|
+
mod.send :remove_method, :nested
|
31
|
+
end
|
32
|
+
|
33
|
+
if mod.instance_methods.include?(:filter)
|
34
|
+
mod.send :alias_method, "filter_#{prefix}_params".to_sym, :filter
|
35
|
+
mod.send :remove_method, :filter
|
36
|
+
end
|
37
|
+
|
38
|
+
application_controller.include mod
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def model_orders
|
43
|
+
if self.respond_to?("#{model.model_name.singular}_orders", true)
|
44
|
+
self.send("#{model.model_name.singular}_orders")
|
45
|
+
else
|
46
|
+
[]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def model_params
|
51
|
+
if self.respond_to?("filter_#{model_name(model)}_params", true)
|
52
|
+
self.send("filter_#{model_name(model)}_params", params[model_name(model)], id: params[:id])
|
53
|
+
else
|
54
|
+
filter_model_params(params[model_name(model)], model.base_class)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def filter_model_params(model_params, model, id: nil, allow_id: nil)
|
59
|
+
permitted_params = if self.respond_to?("#{model_name(model)}_attributes", true)
|
60
|
+
permits = self.send("#{model_name(model)}_attributes")
|
61
|
+
|
62
|
+
allow_id ? model_params.permit(permits, :id) : model_params.permit(permits)
|
63
|
+
else
|
64
|
+
ActionController::Parameters.new
|
65
|
+
end
|
66
|
+
|
67
|
+
if self.respond_to?("nested_#{model_name(model)}_attributes", true)
|
68
|
+
self.send("nested_#{model_name(model)}_attributes").each do |relation|
|
69
|
+
relation = model.reflect_on_association(relation)
|
70
|
+
attributes_key = "#{relation.name}_attributes"
|
71
|
+
|
72
|
+
if model_params.has_key?(attributes_key)
|
73
|
+
filter_method = "filter_#{relation.klass.base_class.model_name.singular}_params"
|
74
|
+
if model_params[attributes_key].nil?
|
75
|
+
permitted_params[attributes_key] = nil
|
76
|
+
elsif model_params[attributes_key].is_a?(Array) && model_params[attributes_key].all? { |a| a.keys.map(&:to_sym) == [:id] }
|
77
|
+
permitted_params["#{relation.name.to_s.singularize}_ids"] = model_params[attributes_key].map{|a| a['id']}
|
78
|
+
elsif self.respond_to?(filter_method, true)
|
79
|
+
permitted_params[attributes_key] = if model_params[attributes_key].is_a?(Array)
|
80
|
+
model_params[attributes_key].map { |i| self.send(filter_method, i, allow_id: true) }
|
81
|
+
else
|
82
|
+
self.send(filter_method, model_params[attributes_key], allow_id: true)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
permitted_params[attributes_key] = if model_params[attributes_key].is_a?(Array)
|
86
|
+
model_params[attributes_key].map { |i| filter_model_params(i, relation.klass.base_class, allow_id: true) }
|
87
|
+
else
|
88
|
+
filter_model_params(model_params[attributes_key], relation.klass.base_class, allow_id: true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
elsif relation.collection? && model_params.has_key?("#{relation.name.to_s.singularize}_ids")
|
92
|
+
permitted_params["#{relation.name.to_s.singularize}_ids"] = model_params["#{relation.name.to_s.singularize}_ids"]
|
93
|
+
elsif model_params.has_key?(relation.foreign_key)
|
94
|
+
permitted_params[relation.foreign_key] = model_params[relation.foreign_key]
|
95
|
+
permitted_params[relation.foreign_type] = model_params[relation.foreign_type] if relation.polymorphic?
|
96
|
+
end
|
97
|
+
|
98
|
+
permitted_params.permit!
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
permitted_params
|
103
|
+
end
|
104
|
+
|
105
|
+
def model_name(model)
|
106
|
+
if model.model_name.singular.starts_with?('habtm_')
|
107
|
+
model.reflect_on_all_associations.map { |a| a.klass.base_class.model_name.singular }.sort.join('_')
|
108
|
+
else
|
109
|
+
model.model_name.singular
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class PostgreSQLAdapter < AbstractAdapter
|
4
|
+
|
5
|
+
# Returns a comment stored in database for given table
|
6
|
+
def database_comment(database_name=nil) # :nodoc:
|
7
|
+
database_name ||= current_database
|
8
|
+
|
9
|
+
scope = quoted_scope(database_name, type: "BASE TABLE")
|
10
|
+
if scope[:name]
|
11
|
+
query_value(<<~SQL, "SCHEMA")
|
12
|
+
SELECT pg_catalog.shobj_description(d.oid, 'pg_database')
|
13
|
+
FROM pg_catalog.pg_database d
|
14
|
+
WHERE datname = #{scope[:name]};
|
15
|
+
SQL
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,10 +1,13 @@
|
|
1
1
|
module StandardAPI
|
2
2
|
module Controller
|
3
3
|
|
4
|
+
delegate :preloadables, to: :helpers
|
5
|
+
|
4
6
|
def self.included(klass)
|
5
|
-
klass.helper_method :includes, :orders, :model, :resource_limit,
|
6
|
-
:default_limit
|
7
|
+
klass.helper_method :includes, :orders, :model, :models, :resource_limit,
|
8
|
+
:default_limit
|
7
9
|
klass.before_action :set_standardapi_headers
|
10
|
+
klass.rescue_from StandardAPI::ParameterMissing, with: :bad_request
|
8
11
|
klass.rescue_from StandardAPI::UnpermittedParameters, with: :bad_request
|
9
12
|
klass.append_view_path(File.join(File.dirname(__FILE__), 'views'))
|
10
13
|
klass.extend(ClassMethods)
|
@@ -13,11 +16,17 @@ module StandardAPI
|
|
13
16
|
def tables
|
14
17
|
Rails.application.eager_load! if !Rails.application.config.eager_load
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
render json:
|
19
|
+
tables = ApplicationController.descendants
|
20
|
+
tables.select! { |c| c.ancestors.include?(self.class) && c != self.class }
|
21
|
+
tables.map!(&:model).compact!
|
22
|
+
tables.map!(&:table_name)
|
23
|
+
render json: tables
|
24
|
+
end
|
25
|
+
|
26
|
+
if Rails.env == 'development'
|
27
|
+
def schema
|
28
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
29
|
+
end
|
21
30
|
end
|
22
31
|
|
23
32
|
def index
|
@@ -34,7 +43,7 @@ module StandardAPI
|
|
34
43
|
end
|
35
44
|
end
|
36
45
|
@calculations = Hash[@calculations] if @calculations[0].is_a?(Array) && params[:group_by]
|
37
|
-
|
46
|
+
|
38
47
|
render json: @calculations
|
39
48
|
end
|
40
49
|
|
@@ -95,32 +104,40 @@ module StandardAPI
|
|
95
104
|
resources.find(params[:id]).destroy!
|
96
105
|
head :no_content
|
97
106
|
end
|
98
|
-
|
107
|
+
|
99
108
|
def remove_resource
|
100
109
|
resource = resources.find(params[:id])
|
101
|
-
|
102
|
-
subresource =
|
103
|
-
|
110
|
+
association = resource.association(params[:relationship])
|
111
|
+
subresource = association.klass.find_by_id(params[:resource_id])
|
112
|
+
|
104
113
|
if(subresource)
|
105
|
-
|
106
|
-
|
114
|
+
if association.is_a? ActiveRecord::Associations::HasManyAssociation
|
115
|
+
resource.send(params[:relationship]).delete(subresource)
|
116
|
+
else
|
117
|
+
resource.send("#{params[:relationship]}=", nil)
|
118
|
+
end
|
119
|
+
head :no_content
|
107
120
|
else
|
108
121
|
head :not_found
|
109
122
|
end
|
110
123
|
end
|
111
|
-
|
124
|
+
|
112
125
|
def add_resource
|
113
126
|
resource = resources.find(params[:id])
|
114
|
-
|
115
|
-
|
116
|
-
|
127
|
+
association = resource.association(params[:relationship])
|
128
|
+
subresource = association.klass.find_by_id(params[:resource_id])
|
129
|
+
|
117
130
|
if(subresource)
|
118
|
-
|
131
|
+
if association.is_a? ActiveRecord::Associations::HasManyAssociation
|
132
|
+
result = resource.send(params[:relationship]) << subresource
|
133
|
+
else
|
134
|
+
result = resource.send("#{params[:relationship]}=", subresource)
|
135
|
+
end
|
119
136
|
head result ? :created : :bad_request
|
120
137
|
else
|
121
138
|
head :not_found
|
122
139
|
end
|
123
|
-
|
140
|
+
|
124
141
|
end
|
125
142
|
|
126
143
|
# Override if you want to support masking
|
@@ -129,7 +146,7 @@ module StandardAPI
|
|
129
146
|
end
|
130
147
|
|
131
148
|
module ClassMethods
|
132
|
-
|
149
|
+
|
133
150
|
def model
|
134
151
|
return @model if defined?(@model)
|
135
152
|
@model = name.sub(/Controller\z/, '').singularize.camelize.safe_constantize
|
@@ -151,6 +168,15 @@ module StandardAPI
|
|
151
168
|
self.class.model
|
152
169
|
end
|
153
170
|
|
171
|
+
def models
|
172
|
+
return @models if defined?(@models)
|
173
|
+
Rails.application.eager_load! if !Rails.application.config.eager_load
|
174
|
+
|
175
|
+
@models = ApplicationController.descendants
|
176
|
+
@models.select! { |c| c.ancestors.include?(self.class) && c != self.class }
|
177
|
+
@models.map!(&:model).compact!
|
178
|
+
end
|
179
|
+
|
154
180
|
def model_includes
|
155
181
|
if self.respond_to?("#{model.model_name.singular}_includes", true)
|
156
182
|
self.send("#{model.model_name.singular}_includes")
|
@@ -171,7 +197,7 @@ module StandardAPI
|
|
171
197
|
if self.respond_to?("#{model.model_name.singular}_params", true)
|
172
198
|
params.require(model.model_name.singular).permit(self.send("#{model.model_name.singular}_params"))
|
173
199
|
else
|
174
|
-
|
200
|
+
ActionController::Parameters.new
|
175
201
|
end
|
176
202
|
end
|
177
203
|
|
@@ -189,82 +215,41 @@ module StandardAPI
|
|
189
215
|
end
|
190
216
|
|
191
217
|
def resources
|
192
|
-
|
193
|
-
|
218
|
+
mask = current_mask[model.table_name] || current_mask[model.table_name.to_sym]
|
219
|
+
query = model.filter(params['where']).filter(mask)
|
220
|
+
|
194
221
|
if params[:distinct_on]
|
195
222
|
query = query.distinct_on(params[:distinct_on])
|
196
223
|
elsif params[:distinct]
|
197
224
|
query = query.distinct
|
198
225
|
end
|
199
|
-
|
226
|
+
|
200
227
|
if params[:join]
|
201
228
|
query = query.joins(params[:join].to_sym)
|
202
229
|
end
|
203
|
-
|
230
|
+
|
204
231
|
if params[:group_by]
|
205
232
|
query = query.group(params[:group_by])
|
206
233
|
end
|
207
|
-
|
234
|
+
|
208
235
|
query
|
209
236
|
end
|
210
237
|
|
211
238
|
def includes
|
212
239
|
@includes ||= StandardAPI::Includes.sanitize(params[:include], model_includes)
|
213
240
|
end
|
214
|
-
|
215
|
-
def preloadables(record, includes)
|
216
|
-
preloads = {}
|
217
|
-
|
218
|
-
includes.each do |key, value|
|
219
|
-
if reflection = record.klass.reflections[key]
|
220
|
-
case value
|
221
|
-
when true
|
222
|
-
preloads[key] = value
|
223
|
-
when Hash, ActiveSupport::HashWithIndifferentAccess
|
224
|
-
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
225
|
-
if !reflection.polymorphic?
|
226
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
preloads.empty? ? record : record.preload(preloads)
|
234
|
-
end
|
235
|
-
|
236
|
-
def preloadables_hash(klass, iclds)
|
237
|
-
preloads = {}
|
238
|
-
|
239
|
-
iclds.each do |key, value|
|
240
|
-
if reflection = klass.reflections[key]
|
241
|
-
case value
|
242
|
-
when true
|
243
|
-
preloads[key] = value
|
244
|
-
when Hash, ActiveSupport::HashWithIndifferentAccess
|
245
|
-
if !value.keys.any? { |x| ['when', 'where', 'limit', 'offset', 'order', 'distinct'].include?(x) }
|
246
|
-
if !reflection.polymorphic?
|
247
|
-
preloads[key] = preloadables_hash(reflection.klass, value)
|
248
|
-
end
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
preloads
|
255
|
-
end
|
256
|
-
|
241
|
+
|
257
242
|
def required_orders
|
258
243
|
[]
|
259
244
|
end
|
260
|
-
|
245
|
+
|
261
246
|
def default_orders
|
262
247
|
nil
|
263
248
|
end
|
264
249
|
|
265
250
|
def orders
|
266
251
|
exluded_required_orders = required_orders.map(&:to_s)
|
267
|
-
|
252
|
+
|
268
253
|
case params[:order]
|
269
254
|
when Hash, ActionController::Parameters
|
270
255
|
exluded_required_orders -= params[:order].keys.map(&:to_s)
|
@@ -280,7 +265,7 @@ module StandardAPI
|
|
280
265
|
when String
|
281
266
|
exluded_required_orders.delete(params[:order])
|
282
267
|
end
|
283
|
-
|
268
|
+
|
284
269
|
if !exluded_required_orders.empty?
|
285
270
|
params[:order] = exluded_required_orders.unshift(params[:order])
|
286
271
|
end
|
@@ -309,9 +294,9 @@ module StandardAPI
|
|
309
294
|
limit = params.permit(:limit)[:limit]&.to_i || default_limit
|
310
295
|
|
311
296
|
if !limit
|
312
|
-
raise
|
297
|
+
raise StandardAPI::ParameterMissing.new(:limit)
|
313
298
|
elsif limit > resource_limit
|
314
|
-
raise
|
299
|
+
raise StandardAPI::UnpermittedParameters.new([:limit, limit])
|
315
300
|
end
|
316
301
|
|
317
302
|
limit
|
@@ -333,16 +318,28 @@ module StandardAPI
|
|
333
318
|
@selects = []
|
334
319
|
@selects << params[:group_by] if params[:group_by]
|
335
320
|
Array(params[:select]).each do |select|
|
336
|
-
select.each do |func,
|
321
|
+
select.each do |func, value|
|
322
|
+
distinct = false
|
323
|
+
|
324
|
+
column = case value
|
325
|
+
when ActionController::Parameters
|
326
|
+
# TODO: Add support for other aggregate expressions
|
327
|
+
# https://www.postgresql.org/docs/current/sql-expressions.html#SYNTAX-AGGREGATES
|
328
|
+
distinct = !value[:distinct].nil?
|
329
|
+
value[:distinct]
|
330
|
+
else
|
331
|
+
value
|
332
|
+
end
|
333
|
+
|
337
334
|
if (parts = column.split(".")).length > 1
|
338
335
|
@model = parts[0].singularize.camelize.constantize
|
339
336
|
column = parts[1]
|
340
337
|
end
|
341
|
-
|
338
|
+
|
342
339
|
column = column == '*' ? Arel.star : column.to_sym
|
343
340
|
if functions.include?(func.to_s.downcase)
|
344
341
|
node = (defined?(@model) ? @model : model).arel_table[column].send(func)
|
345
|
-
node.distinct =
|
342
|
+
node.distinct = distinct
|
346
343
|
@selects << node
|
347
344
|
end
|
348
345
|
end
|