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
@@ -0,0 +1,82 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module ActiveRecord
|
4
|
+
class ARecLowLevelCreator
|
5
|
+
attr_reader :primary_key
|
6
|
+
|
7
|
+
def initialize(adapter_klass, original_attributes)
|
8
|
+
self.adapter_klass = adapter_klass
|
9
|
+
self.failed = false
|
10
|
+
self.original_attributes = original_attributes
|
11
|
+
self.attrs = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def map(attributes, opts)
|
15
|
+
if opts[:foreign_key]
|
16
|
+
attributes.merge!(opts[:foreign_key])
|
17
|
+
end
|
18
|
+
|
19
|
+
if opts[:primary] && original_attributes[:__type__]
|
20
|
+
attributes.merge!(
|
21
|
+
original_attributes[:__type__][:name] => original_attributes[:__type__][:value]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
record = record_class(opts[:to]).create(attributes)
|
26
|
+
|
27
|
+
if record.persisted?
|
28
|
+
attrs.merge!(record.attributes)
|
29
|
+
|
30
|
+
if opts[:primary]
|
31
|
+
self.primary_key = record.send(adapter_klass.primary_key_name)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
self.failed = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def map_array(h, opts)
|
39
|
+
h.each do |key, items|
|
40
|
+
attrs[key] = items.map do |attributes|
|
41
|
+
if opts[:foreign_key]
|
42
|
+
attributes.merge!(opts[:foreign_key])
|
43
|
+
end
|
44
|
+
|
45
|
+
record = record_class(opts[:to]).create(attributes)
|
46
|
+
|
47
|
+
if record.persisted?
|
48
|
+
attributes.merge(id: record.id)
|
49
|
+
else
|
50
|
+
self.failed = true
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def success?
|
58
|
+
!failed
|
59
|
+
end
|
60
|
+
|
61
|
+
def failed!
|
62
|
+
self.failed = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def result
|
66
|
+
original_attributes.symbolize_keys.merge(attrs.merge(
|
67
|
+
adapter_klass.primary_key_name => primary_key
|
68
|
+
).symbolize_keys)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_accessor :adapter_klass, :failed, :original_attributes, :attrs, :mapper_klass
|
74
|
+
attr_writer :primary_key
|
75
|
+
|
76
|
+
def record_class(name)
|
77
|
+
adapter_klass.record_classes[name]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module ActiveRecord
|
4
|
+
class ARecLowLevelDeleter
|
5
|
+
def initialize(adapter_klass, original_attributes)
|
6
|
+
self.adapter_klass = adapter_klass
|
7
|
+
self.failed = false
|
8
|
+
self.original_attributes = original_attributes
|
9
|
+
self.attrs = {}
|
10
|
+
self.calls = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def map(attributes, opts)
|
14
|
+
record = record_class(opts[:to])
|
15
|
+
|
16
|
+
if opts[:foreign_key]
|
17
|
+
record = record.where(opts[:foreign_key])
|
18
|
+
end
|
19
|
+
|
20
|
+
if opts[:primary]
|
21
|
+
record = record.where(
|
22
|
+
adapter_klass.primary_key_name => primary_key
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
calls.unshift -> {
|
27
|
+
record.delete_all == 1
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def map_array(h, opts)
|
32
|
+
raise ArgumentError, 'map_array requires opts[foreign_key] to be set' unless opts[:foreign_key]
|
33
|
+
|
34
|
+
h.each do |key, items|
|
35
|
+
record = record_class(opts[:to])
|
36
|
+
query = record.where(opts[:foreign_key])
|
37
|
+
|
38
|
+
remove_ids = record.pluck(:id)
|
39
|
+
|
40
|
+
calls.unshift -> {
|
41
|
+
query.where(id: remove_ids).delete_all == remove_ids.count
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def success?
|
47
|
+
calls.all? do |call|
|
48
|
+
call.call
|
49
|
+
end && !failed
|
50
|
+
end
|
51
|
+
|
52
|
+
def failed!
|
53
|
+
self.failed = true
|
54
|
+
end
|
55
|
+
|
56
|
+
def result
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
|
60
|
+
def primary_key
|
61
|
+
original_attributes[adapter_klass.primary_key_name]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_accessor :adapter_klass, :failed, :mapper_klass, :original_attributes, :attrs, :calls
|
67
|
+
|
68
|
+
def record_class(name)
|
69
|
+
adapter_klass.record_classes[name]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module ActiveRecord
|
4
|
+
class ARecLowLevelUpdater
|
5
|
+
def initialize(adapter_klass, original_attributes)
|
6
|
+
self.adapter_klass = adapter_klass
|
7
|
+
self.failed = false
|
8
|
+
self.original_attributes = original_attributes
|
9
|
+
self.attrs = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def map(attributes, opts)
|
13
|
+
record = record_class(opts[:to])
|
14
|
+
|
15
|
+
if opts[:foreign_key]
|
16
|
+
record = record.where(opts[:foreign_key])
|
17
|
+
end
|
18
|
+
|
19
|
+
if opts[:primary]
|
20
|
+
record = record.where(
|
21
|
+
adapter_klass.primary_key_name => primary_key
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
result = record.update_all(attributes)
|
26
|
+
|
27
|
+
if result == 0
|
28
|
+
self.failed = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def map_array(h, opts)
|
33
|
+
raise ArgumentError, 'map_array requires opts[foreign_key] to be set' unless opts[:foreign_key]
|
34
|
+
|
35
|
+
h.each do |key, items|
|
36
|
+
record = record_class(opts[:to])
|
37
|
+
query = record.where(opts[:foreign_key])
|
38
|
+
|
39
|
+
ids = record.pluck(:id)
|
40
|
+
remaining_ids = items.map { |i| i[:id] }.compact
|
41
|
+
remove_ids = ids - remaining_ids
|
42
|
+
|
43
|
+
query.where(id: remove_ids).delete_all
|
44
|
+
|
45
|
+
attrs[key] = items.map do |item|
|
46
|
+
item = item.merge(opts[:foreign_key])
|
47
|
+
|
48
|
+
if item[:id]
|
49
|
+
result = query.where(id: item[:id]).update_all(item)
|
50
|
+
|
51
|
+
if result != 1
|
52
|
+
self.failed = true
|
53
|
+
else
|
54
|
+
item
|
55
|
+
end
|
56
|
+
else
|
57
|
+
result = record.create(item)
|
58
|
+
|
59
|
+
if !result.persisted?
|
60
|
+
self.failed = true
|
61
|
+
else
|
62
|
+
result.attributes.symbolize_keys
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def success?
|
70
|
+
!failed
|
71
|
+
end
|
72
|
+
|
73
|
+
def failed!
|
74
|
+
self.failed = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def result
|
78
|
+
r = original_attributes.symbolize_keys.merge(attrs.merge(
|
79
|
+
adapter_klass.primary_key_name => primary_key
|
80
|
+
).symbolize_keys)
|
81
|
+
|
82
|
+
if original_attributes[:__type__]
|
83
|
+
r.merge!(
|
84
|
+
original_attributes[:__type__][:name] => original_attributes[:__type__][:value]
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
r
|
89
|
+
end
|
90
|
+
|
91
|
+
def primary_key
|
92
|
+
original_attributes[adapter_klass.primary_key_name]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
attr_accessor :adapter_klass, :failed, :mapper_klass, :original_attributes, :attrs
|
98
|
+
|
99
|
+
def record_class(name)
|
100
|
+
adapter_klass.record_classes[name]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module FilterHelpers
|
4
|
+
def initialize(memo, filter)
|
5
|
+
self.memo = memo
|
6
|
+
self.filter_params = filter
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter
|
10
|
+
filter_params.inject(memo) do |memo, (attr, filter)|
|
11
|
+
filter_attributes = filter.is_a?(Hash) ? filter : filter.filter_attributes
|
12
|
+
filter_attributes.inject(memo) do |memo, (comparator, value)|
|
13
|
+
if value.nil?
|
14
|
+
memo
|
15
|
+
else
|
16
|
+
case comparator.to_s.downcase
|
17
|
+
when 'eq'
|
18
|
+
equal_filter(memo, attr, value)
|
19
|
+
when 'not_eq'
|
20
|
+
not_equal_filter(memo, attr, value)
|
21
|
+
when 'in'
|
22
|
+
in_filter(memo, attr, value)
|
23
|
+
when 'not_in'
|
24
|
+
not_in_filter(memo, attr, value)
|
25
|
+
when 'lt'
|
26
|
+
less_than_filter(memo, attr, value)
|
27
|
+
when 'gt'
|
28
|
+
greater_than_filter(memo, attr, value)
|
29
|
+
when 'lte'
|
30
|
+
less_than_or_equal_filter(memo, attr, value)
|
31
|
+
when 'gte'
|
32
|
+
greater_than_or_equal_filter(memo, attr, value)
|
33
|
+
when 'not_lt'
|
34
|
+
greater_than_or_equal_filter(memo, attr, value)
|
35
|
+
when 'not_gt'
|
36
|
+
less_than_or_equal_filter(memo, attr, value)
|
37
|
+
when 'not_lte'
|
38
|
+
greater_than_filter(memo, attr, value)
|
39
|
+
when 'not_gte'
|
40
|
+
less_than_filter(memo, attr, value)
|
41
|
+
else
|
42
|
+
if respond_to?("#{comparator.to_s.downcase}_filter", true)
|
43
|
+
send("#{comparator.to_s.downcase}_filter", memo, attr, value)
|
44
|
+
else
|
45
|
+
memo
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_accessor :memo, :filter_params
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module HelpersHelpers
|
4
|
+
def self.included(host)
|
5
|
+
host.send(:extend, SelfMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module SelfMethods
|
9
|
+
def included(host)
|
10
|
+
if !host.is_a?(Quiver::Adapter)
|
11
|
+
host.send(:include, Quiver::Adapter)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def mapper_name
|
19
|
+
@mapper_name ||= self.class.parent.name.split('::').last.underscore
|
20
|
+
end
|
21
|
+
|
22
|
+
def when_valid(model, adapter_op)
|
23
|
+
raise ArgumentError, "requires block" unless block_given?
|
24
|
+
|
25
|
+
errors = model.validate(
|
26
|
+
tags: [adapter_op],
|
27
|
+
mapper: mapper_klass.new,
|
28
|
+
model: model,
|
29
|
+
adapter: self
|
30
|
+
)
|
31
|
+
|
32
|
+
if errors.success?
|
33
|
+
current_time = Time.now.utc
|
34
|
+
model.updated_at = current_time if model.respond_to?(:updated_at=)
|
35
|
+
|
36
|
+
if adapter_op == :create
|
37
|
+
model.created_at ||= current_time if model.respond_to?(:created_at)
|
38
|
+
end
|
39
|
+
|
40
|
+
object = yield(model, errors)
|
41
|
+
end
|
42
|
+
|
43
|
+
Quiver::Mapper::MapperResult.new(object, errors, adapter_op: adapter_op)
|
44
|
+
end
|
45
|
+
|
46
|
+
def hydrate(attributes)
|
47
|
+
mapper_klass.hydrate(attributes).tap do |obj|
|
48
|
+
obj.persisted_by!(adapter_type)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Adapter
|
3
|
+
module MemoryAdapterFilterDefaults
|
4
|
+
include FilterHelpers
|
5
|
+
|
6
|
+
def equal_filter(memo, attr, value)
|
7
|
+
memo.select do |obj|
|
8
|
+
obj[attr.to_sym] == value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def not_equal_filter(memo, attr, value)
|
13
|
+
memo.select do |obj|
|
14
|
+
obj[attr.to_sym] != value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def in_filter(memo, attr, value)
|
19
|
+
memo.select do |obj|
|
20
|
+
value.include?(obj[attr.to_sym])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def not_in_filter(memo, attr, value)
|
25
|
+
memo.select do |obj|
|
26
|
+
!value.include?(obj[attr.to_sym])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def less_than_filter(memo, attr, value)
|
31
|
+
memo.select do |obj|
|
32
|
+
obj[attr.to_sym] < value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def greater_than_filter(memo, attr, value)
|
37
|
+
memo.select do |obj|
|
38
|
+
obj[attr.to_sym] > value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def less_than_or_equal_filter(memo, attr, value)
|
43
|
+
memo.select do |obj|
|
44
|
+
obj[attr.to_sym] <= value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def greater_than_or_equal_filter(memo, attr, value)
|
49
|
+
memo.select do |obj|
|
50
|
+
obj[attr.to_sym] >= value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def nil_filter(memo, attr, value)
|
55
|
+
memo.select do |obj|
|
56
|
+
obj[attr.to_sym].nil? == (value == 'true' || value == true)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def not_nil_filter(memo, attr, value)
|
61
|
+
memo.select do |obj|
|
62
|
+
obj[attr.to_sym].nil? != (value == 'true' || value == true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class MemoryAdapterFilter
|
68
|
+
include MemoryAdapterFilterDefaults
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|