quiver 0.21.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +6 -0
- data/README.md +22 -0
- data/Rakefile +17 -0
- data/bin/quiver +9 -0
- data/lib/quiver.rb +46 -0
- data/lib/quiver/abstract_action.rb +31 -0
- data/lib/quiver/action.rb +208 -0
- data/lib/quiver/action/filter_error.rb +33 -0
- data/lib/quiver/action/filter_value.rb +152 -0
- data/lib/quiver/action/invalid_request_body_error.rb +30 -0
- data/lib/quiver/action/pagination_link_builder.rb +67 -0
- data/lib/quiver/adapter.rb +51 -0
- data/lib/quiver/adapter/active_record_adapter_filter.rb +102 -0
- data/lib/quiver/adapter/active_record_helpers.rb +258 -0
- data/lib/quiver/adapter/arec_low_level_creator.rb +82 -0
- data/lib/quiver/adapter/arec_low_level_deleter.rb +74 -0
- data/lib/quiver/adapter/arec_low_level_updater.rb +105 -0
- data/lib/quiver/adapter/filter_helpers.rb +58 -0
- data/lib/quiver/adapter/helpers_helpers.rb +53 -0
- data/lib/quiver/adapter/memory_adapter_filter.rb +71 -0
- data/lib/quiver/adapter/memory_adapter_store.rb +34 -0
- data/lib/quiver/adapter/memory_helpers.rb +182 -0
- data/lib/quiver/adapter/memory_uuid_primary_key.rb +25 -0
- data/lib/quiver/application.rb +128 -0
- data/lib/quiver/cli/app.rb +25 -0
- data/lib/quiver/cli/generators/endpoint.rb +17 -0
- data/lib/quiver/cli/generators/new_application.rb +166 -0
- data/lib/quiver/cli/generators/new_application_cli.rb +25 -0
- data/lib/quiver/cli/server.rb +37 -0
- data/lib/quiver/cli/templates/Gemfile.tt +8 -0
- data/lib/quiver/cli/templates/Rakefile.tt +12 -0
- data/lib/quiver/cli/templates/config.tt +3 -0
- data/lib/quiver/cli/templates/config/database.tt +21 -0
- data/lib/quiver/cli/templates/gemspec.tt +33 -0
- data/lib/quiver/cli/templates/gitignore.tt +10 -0
- data/lib/quiver/cli/templates/lib/application.tt +14 -0
- data/lib/quiver/cli/templates/lib/application/config/router.tt +11 -0
- data/lib/quiver/cli/templates/lib/application/version.tt +3 -0
- data/lib/quiver/cli/templates/spec/spec_helper.tt +19 -0
- data/lib/quiver/duty.rb +34 -0
- data/lib/quiver/duty_master.rb +23 -0
- data/lib/quiver/duty_master/delayed_job_adapter.rb +15 -0
- data/lib/quiver/duty_master/memory_adapter.rb +18 -0
- data/lib/quiver/duty_test_helper.rb +23 -0
- data/lib/quiver/duty_test_helper/delayed_job_helper.rb +9 -0
- data/lib/quiver/duty_test_helper/memory_helper.rb +15 -0
- data/lib/quiver/error.rb +24 -0
- data/lib/quiver/error_collection.rb +60 -0
- data/lib/quiver/json_parser.rb +17 -0
- data/lib/quiver/logger.rb +26 -0
- data/lib/quiver/mapper.rb +311 -0
- data/lib/quiver/mapper/hook.rb +21 -0
- data/lib/quiver/mapper/mapper_result.rb +7 -0
- data/lib/quiver/mapper/not_found_error.rb +27 -0
- data/lib/quiver/mapper/simple_query_builder.rb +70 -0
- data/lib/quiver/mapper/soft_delete.rb +15 -0
- data/lib/quiver/mappers.rb +75 -0
- data/lib/quiver/middleware_stack.rb +35 -0
- data/lib/quiver/model.rb +63 -0
- data/lib/quiver/model/soft_delete.rb +14 -0
- data/lib/quiver/model/validation_error.rb +27 -0
- data/lib/quiver/model/validations.rb +45 -0
- data/lib/quiver/patcher.rb +94 -0
- data/lib/quiver/result.rb +44 -0
- data/lib/quiver/route_helper.rb +16 -0
- data/lib/quiver/router.rb +37 -0
- data/lib/quiver/serialization.rb +6 -0
- data/lib/quiver/serialization/json_api.rb +7 -0
- data/lib/quiver/serialization/json_api/item_type_handler.rb +96 -0
- data/lib/quiver/serialization/json_api/serializer.rb +77 -0
- data/lib/quiver/tasks.rb +31 -0
- data/lib/quiver/validator.rb +83 -0
- data/lib/quiver/validators/base.rb +21 -0
- data/lib/quiver/validators/presence.rb +34 -0
- data/lib/quiver/validators/unique.rb +33 -0
- data/lib/quiver/version.rb +3 -0
- data/quiver.gemspec +42 -0
- metadata +393 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c7a72a172701aa86d29e34e207afa3b9c366a6e6
|
4
|
+
data.tar.gz: 44c395d87a9a5c24674c9485df4612d765fba9ca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc3433511ef6ee74cf5a13cdc1cdcb76720de132b9806ede1f5f3888c8af6d9b5cdd615f6b8d5edfc35e583e4bf7df94c318bf1edea3f5dca40330cd8cc47da8
|
7
|
+
data.tar.gz: d2b8c3d2763cee663cc2544cee1a5dc3d694fb2c1632718d87560b3f65ab5beed6187a5d06823dd8c61fd281b5ce67746d9e28b967be73e4116629014b65cb51
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Quiver
|
2
|
+
_Sponsored by NCC Group Domain Services_
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
$ gem install quiver
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
`quiver new app_name`
|
11
|
+
|
12
|
+
## Todo
|
13
|
+
|
14
|
+
Actually document things.
|
15
|
+
|
16
|
+
## Contributing
|
17
|
+
|
18
|
+
1. Fork it ( https://github.com/[my-github-username]/quiver/fork )
|
19
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
20
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
21
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
22
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
task :environment do
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Drop, create and migrate the dummy app database'
|
12
|
+
task :generate_dummy_schema do
|
13
|
+
ENV['BUNDLE_GEMFILE'] = File.absolute_path(ENV['BUNDLE_GEMFILE']) if ENV['BUNDLE_GEMFILE']
|
14
|
+
cd 'spec/dummy' do
|
15
|
+
sh 'rake db:drop db:create db:migrate'
|
16
|
+
end
|
17
|
+
end
|
data/bin/quiver
ADDED
data/lib/quiver.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
ENV['RACK_ENV'] ||= 'development'
|
2
|
+
|
3
|
+
require 'quiver/version'
|
4
|
+
require 'pry'
|
5
|
+
require 'active_support/all'
|
6
|
+
|
7
|
+
require 'lotus/router'
|
8
|
+
require 'lotus/controller'
|
9
|
+
|
10
|
+
Lotus::Controller.configure do
|
11
|
+
format json_api: 'application/vnd.api+json'
|
12
|
+
handle_exceptions false
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'quiver/logger'
|
16
|
+
|
17
|
+
module Quiver
|
18
|
+
def self.controller(s)
|
19
|
+
Lotus::Controller.duplicate(s)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'quiver/tasks'
|
24
|
+
require 'quiver/json_parser'
|
25
|
+
require 'quiver/router'
|
26
|
+
require 'quiver/adapter/memory_adapter_store'
|
27
|
+
require 'quiver/application'
|
28
|
+
require 'quiver/error'
|
29
|
+
require 'quiver/error_collection'
|
30
|
+
require 'quiver/validator'
|
31
|
+
require 'quiver/model'
|
32
|
+
require 'quiver/result'
|
33
|
+
require 'quiver/mapper'
|
34
|
+
require 'quiver/mappers'
|
35
|
+
require 'quiver/adapter'
|
36
|
+
require 'quiver/duty'
|
37
|
+
require 'quiver/duty_master'
|
38
|
+
require 'quiver/duty_test_helper'
|
39
|
+
require 'quiver/middleware_stack'
|
40
|
+
|
41
|
+
require 'quiver/abstract_action'
|
42
|
+
require 'quiver/action'
|
43
|
+
require 'quiver/patcher'
|
44
|
+
require 'quiver/serialization'
|
45
|
+
|
46
|
+
require 'quiver/cli/app'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Quiver
|
2
|
+
module AbstractAction
|
3
|
+
def self.included(host)
|
4
|
+
host.send(:include, Lotus::Action)
|
5
|
+
host.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def serializer(val = nil)
|
10
|
+
if val
|
11
|
+
@serializer = val
|
12
|
+
else
|
13
|
+
@serializer || raise("#{self.name} serializer not set")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(params)
|
19
|
+
# because ruby < 2.2.0, pry, and Module.prepend aren't friends
|
20
|
+
internal_call(params)
|
21
|
+
end
|
22
|
+
|
23
|
+
def arrayify(arg)
|
24
|
+
if arg.is_a?(Array)
|
25
|
+
arg
|
26
|
+
else
|
27
|
+
[arg]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Action
|
3
|
+
def self.included(host)
|
4
|
+
host.send(:include, AbstractAction)
|
5
|
+
host.send(:extend, ClassMethods)
|
6
|
+
|
7
|
+
# prepend over Lotus's prepends
|
8
|
+
host.send(:prepend, DurationTracking)
|
9
|
+
host.send(:prepend, Logging)
|
10
|
+
end
|
11
|
+
|
12
|
+
module DurationTracking
|
13
|
+
def call(params)
|
14
|
+
start_duration_tracking
|
15
|
+
super(params)
|
16
|
+
ensure
|
17
|
+
finish_duration_tracking
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Logging
|
22
|
+
def call(params)
|
23
|
+
super(params)
|
24
|
+
ensure
|
25
|
+
if params == nil
|
26
|
+
raise '#params is nil inside of a Quiver::Action. Something probably went wrong internally.'
|
27
|
+
end
|
28
|
+
|
29
|
+
logging_fields = default_logging_fields
|
30
|
+
|
31
|
+
self.class.send(:extra_logging_blocks).reverse.each do |block|
|
32
|
+
logging_fields.merge!(instance_exec(&block))
|
33
|
+
end
|
34
|
+
|
35
|
+
logger.info(logging_fields.merge(extra_logging_fields))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def extra_logging(&block)
|
41
|
+
extra_logging_blocks << block
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def extra_logging_blocks
|
47
|
+
@extra_logging_blocks ||= []
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def internal_call(params)
|
52
|
+
self.params = params
|
53
|
+
|
54
|
+
if params.raw[:terrible_hack].is_a?(JSON::ParserError)
|
55
|
+
return serialize_with(errors: [Quiver::Action::InvalidRequestBodyError.new])
|
56
|
+
end
|
57
|
+
|
58
|
+
serialize_with(run_action)
|
59
|
+
rescue Quiver::Error => e
|
60
|
+
serialize_with(errors: [e])
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_accessor :duration_ms, :db_duration_ms, :duration_start_time, :params
|
66
|
+
|
67
|
+
# for hooking in without prepend
|
68
|
+
def run_action
|
69
|
+
action
|
70
|
+
end
|
71
|
+
|
72
|
+
def start_duration_tracking
|
73
|
+
self.duration_start_time = Time.now
|
74
|
+
|
75
|
+
if defined?(ActiveRecord)
|
76
|
+
ActiveRecord::LogSubscriber.reset_runtime
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def finish_duration_tracking
|
81
|
+
if defined?(ActiveRecord) && ActiveRecord::Base.connected?
|
82
|
+
self.db_duration_ms = ActiveRecord::LogSubscriber.reset_runtime
|
83
|
+
else
|
84
|
+
self.db_duration_ms = 0
|
85
|
+
end
|
86
|
+
|
87
|
+
self.duration_ms = (Time.now - duration_start_time) * 1000 - db_duration_ms
|
88
|
+
end
|
89
|
+
|
90
|
+
def extra_logging_fields
|
91
|
+
{}
|
92
|
+
end
|
93
|
+
|
94
|
+
def default_logging_fields
|
95
|
+
{
|
96
|
+
method: params.env['REQUEST_METHOD'],
|
97
|
+
path: request_path_with_query,
|
98
|
+
controller: self.class.to_s.split('::').first.underscore,
|
99
|
+
action: self.class.to_s.split('::').last.underscore,
|
100
|
+
status: @_status || self.class::DEFAULT_RESPONSE_CODE,
|
101
|
+
ip: request.ip,
|
102
|
+
route: "#{self.class.to_s.split('::').first.underscore}##{self.class.to_s.split('::').last.underscore}",
|
103
|
+
request_id: request_id,
|
104
|
+
tags: [:request],
|
105
|
+
duration: duration_ms.round(1),
|
106
|
+
db: db_duration_ms.round(1) || 0,
|
107
|
+
'@timestamp' => Time.now.utc,
|
108
|
+
'@version' => '1'
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def route_helper
|
113
|
+
RouteHelper.new(self.class.parents[-2]::Config::Router.new.send(:router))
|
114
|
+
end
|
115
|
+
|
116
|
+
def logger
|
117
|
+
@logger ||= self.class.parents[-2]::Application.logger
|
118
|
+
end
|
119
|
+
|
120
|
+
def patch_serialize_with(data)
|
121
|
+
if data[:patch_data]
|
122
|
+
self.status = 200
|
123
|
+
self.format = :json_api
|
124
|
+
self.body = data[:patch_data].to_json
|
125
|
+
else
|
126
|
+
data[:patch_errors] = data[:patch_errors].map do |datum|
|
127
|
+
datum.select { |k, _| k == :errors }
|
128
|
+
end
|
129
|
+
|
130
|
+
self.status = 400
|
131
|
+
self.format = :json_api
|
132
|
+
self.body = data[:patch_errors].to_json
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def serialize_with(data)
|
137
|
+
if data.is_a?(Quiver::Result)
|
138
|
+
mapper_result = data
|
139
|
+
data = if data.success?
|
140
|
+
{
|
141
|
+
data: arrayify(data.object)
|
142
|
+
}.tap do |h|
|
143
|
+
h[:pagination_offset] = data.data[:pagination_offset] if data.data.key?(:pagination_offset)
|
144
|
+
h[:pagination_limit] = data.data[:pagination_limit] if data.data.key?(:pagination_limit)
|
145
|
+
h[:total_count] = data.data[:total_count] if data.data.key?(:total_count)
|
146
|
+
end
|
147
|
+
else
|
148
|
+
{errors: data.errors}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
return patch_serialize_with(data) if data.keys.include?(:patch_data) || data.keys.include?(:patch_errors)
|
153
|
+
|
154
|
+
errors = data[:errors] || []
|
155
|
+
|
156
|
+
if errors.count > 0
|
157
|
+
self.status = errors.first.status
|
158
|
+
else
|
159
|
+
if data.keys.count == 0
|
160
|
+
self.status = 204
|
161
|
+
else
|
162
|
+
if mapper_result && data[:data].count == 1 && mapper_result.data[:adapter_op] == :create
|
163
|
+
self.status = 201
|
164
|
+
else
|
165
|
+
self.status = 200
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
self.format = :json_api
|
171
|
+
hash_body = self.class.serializer.new({collections: data}).serialize(context: self)
|
172
|
+
|
173
|
+
hash_body.merge!(
|
174
|
+
links: PaginationLinkBuilder.new(
|
175
|
+
request_path_with_query, data[:pagination_offset], data[:pagination_limit], data[:total_count]
|
176
|
+
).pagination_links
|
177
|
+
) if data.key?(:pagination_offset)
|
178
|
+
|
179
|
+
meta = {}
|
180
|
+
|
181
|
+
if data.key?(:pagination_offset)
|
182
|
+
meta[:page] ||= {}
|
183
|
+
|
184
|
+
meta[:page][:offset] = data[:pagination_offset] if data.key?(:pagination_offset)
|
185
|
+
meta[:page][:limit] = data[:pagination_limit] if data.key?(:pagination_limit) && data[:pagination_limit] != -1
|
186
|
+
meta[:page][:total] = data[:total_count] if data.key?(:total_count)
|
187
|
+
end
|
188
|
+
|
189
|
+
hash_body.merge!(
|
190
|
+
meta: meta
|
191
|
+
)
|
192
|
+
|
193
|
+
self.body = hash_body.to_json
|
194
|
+
end
|
195
|
+
|
196
|
+
def request_path
|
197
|
+
@request_path ||= request.path || ''
|
198
|
+
end
|
199
|
+
|
200
|
+
def request_path_with_query
|
201
|
+
@request_path_with_query ||= request.fullpath || ''
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
require 'quiver/action/filter_value'
|
207
|
+
require 'quiver/action/pagination_link_builder'
|
208
|
+
require 'quiver/action/invalid_request_body_error'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Quiver::Action
|
2
|
+
class FilterError < Quiver::Error
|
3
|
+
attr_reader :detail
|
4
|
+
|
5
|
+
def initialize(detail)
|
6
|
+
self.detail = detail
|
7
|
+
end
|
8
|
+
|
9
|
+
def title
|
10
|
+
'filter_error'
|
11
|
+
end
|
12
|
+
|
13
|
+
def path
|
14
|
+
"/"
|
15
|
+
end
|
16
|
+
|
17
|
+
def status
|
18
|
+
422
|
19
|
+
end
|
20
|
+
|
21
|
+
def code
|
22
|
+
:filter_error
|
23
|
+
end
|
24
|
+
|
25
|
+
def serialization_type
|
26
|
+
'Error'
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_writer :detail
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Action
|
3
|
+
module FilterValue
|
4
|
+
PRESENCE = %w|nil not_nil|.freeze
|
5
|
+
EQUALITIES = %w|eq not_eq|.freeze
|
6
|
+
INCLUSIONS = %w|in not_in|.freeze
|
7
|
+
INEQUALITIES = %w|gt lt gte lte not_gt not_lt not_gte not_lte|.freeze
|
8
|
+
|
9
|
+
def self.with(type, *args, extras:[])
|
10
|
+
set = Set.new(args + extras)
|
11
|
+
klasses[[set, type]] ||= Class.new do
|
12
|
+
include Extant::Attributes
|
13
|
+
include FilterValue
|
14
|
+
|
15
|
+
def self.supported_comparisons
|
16
|
+
@supported_comparisons
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.extra_supported_comparisons
|
20
|
+
@extra_supported_comparisons
|
21
|
+
end
|
22
|
+
end.tap do |klass|
|
23
|
+
klass.instance_variable_set(
|
24
|
+
'@supported_comparisons',
|
25
|
+
set
|
26
|
+
)
|
27
|
+
|
28
|
+
klass.instance_variable_set(
|
29
|
+
'@extra_supported_comparisons',
|
30
|
+
extras
|
31
|
+
)
|
32
|
+
|
33
|
+
extras.each do |extra|
|
34
|
+
klass.send(:attribute, extra, type)
|
35
|
+
end
|
36
|
+
|
37
|
+
if set.include?(:presence)
|
38
|
+
klass.send(:attribute, :nil, String)
|
39
|
+
klass.send(:attribute, :not_nil, String)
|
40
|
+
end
|
41
|
+
|
42
|
+
if set.include?(:equalities)
|
43
|
+
klass.send(:attribute, :eq, type)
|
44
|
+
klass.send(:attribute, :not_eq, type)
|
45
|
+
end
|
46
|
+
|
47
|
+
if set.include?(:inclusions)
|
48
|
+
klass.send(:attribute, :in, Array[type])
|
49
|
+
klass.send(:attribute, :not_in, Array[type])
|
50
|
+
end
|
51
|
+
|
52
|
+
if set.include?(:inequalities)
|
53
|
+
klass.send(:attribute, :gt, type)
|
54
|
+
klass.send(:attribute, :not_gt, type)
|
55
|
+
klass.send(:attribute, :gte, type)
|
56
|
+
klass.send(:attribute, :not_gte, type)
|
57
|
+
klass.send(:attribute, :lt, type)
|
58
|
+
klass.send(:attribute, :not_lt, type)
|
59
|
+
klass.send(:attribute, :lte, type)
|
60
|
+
klass.send(:attribute, :not_lte, type)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.with_all(type, extras:[])
|
66
|
+
with(type, :presence, :equalities, :inequalities, :inclusions)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.klasses
|
70
|
+
@klasses ||= {}
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :errors
|
74
|
+
|
75
|
+
def initialize(filter)
|
76
|
+
self.errors = Quiver::ErrorCollection.new
|
77
|
+
|
78
|
+
filter = filter.to_h if filter.is_a?(Lotus::Utils::Hash)
|
79
|
+
self.filter = filter
|
80
|
+
|
81
|
+
if filter.is_a?(Hash)
|
82
|
+
keys = filter.keys
|
83
|
+
keys.each do |key|
|
84
|
+
filter[key.sub('~', 'not_')] = filter.delete(key)
|
85
|
+
end
|
86
|
+
|
87
|
+
keys.each do |key|
|
88
|
+
if INCLUSIONS.include?(key) && !filter[key].is_a?(Array)
|
89
|
+
errors << FilterError.new("'#{key}' must map to an Array")
|
90
|
+
filter[key] = []
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
(filter.keys - supported_comparisons).each do |key|
|
95
|
+
errors << FilterError.new("'#{key}' is not supported")
|
96
|
+
filter.delete(key)
|
97
|
+
end
|
98
|
+
|
99
|
+
filter
|
100
|
+
else
|
101
|
+
filter = {}
|
102
|
+
errors << FilterError.new('filters must be a Hash')
|
103
|
+
end
|
104
|
+
|
105
|
+
super
|
106
|
+
|
107
|
+
validate
|
108
|
+
end
|
109
|
+
|
110
|
+
def filter_attributes
|
111
|
+
attributes.slice(*filter.keys.map(&:to_sym))
|
112
|
+
end
|
113
|
+
|
114
|
+
def valid?
|
115
|
+
!errors.any?
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
attr_writer :errors
|
121
|
+
attr_accessor :filter
|
122
|
+
|
123
|
+
def validate
|
124
|
+
extant_attributes.each do |key, attr_object|
|
125
|
+
if attr_object.set? && !attr_object.coerced?
|
126
|
+
case
|
127
|
+
when EQUALITIES.include?(attr_object.name.to_s) || INEQUALITIES.include?(attr_object.name.to_s) || PRESENCE.include?(attr_object.name.to_s)
|
128
|
+
errors << FilterError.new("'#{attr_object.name}' must not map to Hashes or Arrays")
|
129
|
+
when INCLUSIONS.include?(attr_object.name.to_s)
|
130
|
+
errors << FilterError.new("'#{attr_object.name}' must map to an Array")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def supported_comparisons
|
137
|
+
unless @supported_comparisons
|
138
|
+
@supported_comparisons = []
|
139
|
+
@supported_comparisons += PRESENCE if self.class.supported_comparisons.include?(:presence)
|
140
|
+
@supported_comparisons += EQUALITIES if self.class.supported_comparisons.include?(:equalities)
|
141
|
+
@supported_comparisons += INEQUALITIES if self.class.supported_comparisons.include?(:inequalities)
|
142
|
+
@supported_comparisons += INCLUSIONS if self.class.supported_comparisons.include?(:inclusions)
|
143
|
+
@supported_comparisons += self.class.extra_supported_comparisons.map(&:to_s)
|
144
|
+
end
|
145
|
+
|
146
|
+
@supported_comparisons
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
require 'quiver/action/filter_error'
|