quiver 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|