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
@@ -0,0 +1,75 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Mappers
|
3
|
+
def self.transaction(&block)
|
4
|
+
raise ArgumentError, "#transaction requires a block" unless block_given?
|
5
|
+
|
6
|
+
root_module = self.parent
|
7
|
+
adapter_type = root_module::Application.default_adapter_type
|
8
|
+
|
9
|
+
transaction_klass = self.const_get("#{adapter_type.to_s.camelize}Transaction")
|
10
|
+
transaction_klass.transaction(root_module, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
class Transaction
|
14
|
+
def initialize
|
15
|
+
self.rollback = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def rollback!
|
19
|
+
self.rollback = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def rollback?
|
23
|
+
rollback
|
24
|
+
end
|
25
|
+
|
26
|
+
def good?
|
27
|
+
!rollback
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_accessor :rollback
|
33
|
+
end
|
34
|
+
|
35
|
+
class RollbackTransaction < StandardError; end
|
36
|
+
|
37
|
+
module MemoryTransaction
|
38
|
+
def self.transaction(root_module, &block)
|
39
|
+
raise ArgumentError, "#transaction requires a block" unless block_given?
|
40
|
+
|
41
|
+
transaction = Transaction.new
|
42
|
+
|
43
|
+
root_module::Application.memory_adapter_store.transaction do
|
44
|
+
ret = block.call(transaction)
|
45
|
+
|
46
|
+
raise RollbackTransaction if transaction.rollback?
|
47
|
+
|
48
|
+
ret
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ActiveRecordTransaction
|
54
|
+
def self.transaction(root_module, &block)
|
55
|
+
raise ArgumentError, "#transaction requires a block" unless block_given?
|
56
|
+
|
57
|
+
transaction = Transaction.new
|
58
|
+
|
59
|
+
ret = nil
|
60
|
+
|
61
|
+
ActiveRecord::Base.transaction do
|
62
|
+
begin
|
63
|
+
ret = block.call(transaction)
|
64
|
+
rescue ::ActiveRecord::ActiveRecordError, RollbackTransaction
|
65
|
+
transaction.rollback!
|
66
|
+
end
|
67
|
+
|
68
|
+
raise ActiveRecord::Rollback if transaction.rollback?
|
69
|
+
end
|
70
|
+
|
71
|
+
ret
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Quiver
|
2
|
+
class MiddlewareStack
|
3
|
+
attr_reader :middlewares
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
self.middlewares = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(middleware)
|
10
|
+
middlewares << middleware
|
11
|
+
@middleware_stack = nil
|
12
|
+
middlewares
|
13
|
+
end
|
14
|
+
|
15
|
+
def unshift(*middleware)
|
16
|
+
middlewares.unshift(*middleware)
|
17
|
+
@middleware_stack = nil
|
18
|
+
middlewares
|
19
|
+
end
|
20
|
+
|
21
|
+
def stack(app)
|
22
|
+
@stack ||= rebuild_stack!(app)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_writer :middlewares
|
28
|
+
|
29
|
+
def rebuild_stack!(app)
|
30
|
+
middlewares.reverse.inject(app) do |app, middleware|
|
31
|
+
middleware.new(app)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/quiver/model.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'extant'
|
2
|
+
|
3
|
+
module Quiver
|
4
|
+
module Model
|
5
|
+
module ExtantAttributeOverrides
|
6
|
+
def with(attrs={}, metadata={})
|
7
|
+
super.tap do |new_instance|
|
8
|
+
persisted_by.each do |pb|
|
9
|
+
new_instance.persisted_by!(pb)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def dirty?(attr=nil)
|
15
|
+
return true unless persisted?
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'quiver/model/validations'
|
22
|
+
|
23
|
+
def self.included(host)
|
24
|
+
host.send(:include, Extant::Attributes)
|
25
|
+
host.send(:include, Validations)
|
26
|
+
host.send(:include, ExtantAttributeOverrides)
|
27
|
+
end
|
28
|
+
|
29
|
+
def coerced?(attr)
|
30
|
+
extant_attributes[attr].set? && extant_attributes[attr].coerced? ||
|
31
|
+
extant_attributes[attr].unset? && !extant_attributes[attr].coerced?
|
32
|
+
end
|
33
|
+
|
34
|
+
def coerced_all?
|
35
|
+
extant_attributes.all? { |(_, attr_object)| attr_object.coerced? }
|
36
|
+
end
|
37
|
+
|
38
|
+
def persisted?
|
39
|
+
persisted_by.any?
|
40
|
+
end
|
41
|
+
|
42
|
+
def persisted_by
|
43
|
+
@persisted_by ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
def persisted_by!(adapter_type)
|
47
|
+
persisted_by << adapter_type
|
48
|
+
end
|
49
|
+
|
50
|
+
def serialization_type
|
51
|
+
@serialization_type ||= self.class.name.split('::').last
|
52
|
+
end
|
53
|
+
|
54
|
+
def original_attributes
|
55
|
+
original_extant_attributes.each_with_object({}) do |(k, v), attrs|
|
56
|
+
attrs[k] = v.value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
require 'quiver/model/validation_error'
|
63
|
+
require 'quiver/model/soft_delete'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Quiver::Model
|
2
|
+
class ValidationError < Quiver::Error
|
3
|
+
def title
|
4
|
+
type
|
5
|
+
end
|
6
|
+
|
7
|
+
def detail
|
8
|
+
type
|
9
|
+
end
|
10
|
+
|
11
|
+
def path
|
12
|
+
"/#{subject}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def status
|
16
|
+
422
|
17
|
+
end
|
18
|
+
|
19
|
+
def code
|
20
|
+
:model_validation_error
|
21
|
+
end
|
22
|
+
|
23
|
+
def serialization_type
|
24
|
+
'Error'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Model
|
3
|
+
module Validations
|
4
|
+
def self.included(host)
|
5
|
+
host.send(:extend, ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def validate(attr_or_proc, options={})
|
10
|
+
validation_definitions << {
|
11
|
+
attr_or_proc: attr_or_proc,
|
12
|
+
options: options
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def validator
|
17
|
+
@validator ||= Quiver::Validator.new(validation_definitions)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def validation_definitions
|
23
|
+
@validation_definitions ||= []
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate(options={})
|
28
|
+
result = self.class.validator.validate(self, options)
|
29
|
+
|
30
|
+
if respond_to?(:extant_attributes, true)
|
31
|
+
extant_attributes.each do |(key, attr_object)|
|
32
|
+
unless coerced?(attr_object.name)
|
33
|
+
result << Quiver::Model::ValidationError.new(
|
34
|
+
attr_object.name,
|
35
|
+
"could_not_be_coerced_to_expected_type.#{attr_object.coercer_name}"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Patcher
|
3
|
+
def self.included(host)
|
4
|
+
host.send(:include, AbstractAction)
|
5
|
+
host.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def values(&block)
|
10
|
+
warn '`values` is no longer supported.'
|
11
|
+
end
|
12
|
+
|
13
|
+
def params(&block)
|
14
|
+
klass = Class.new do
|
15
|
+
include Lotus::Validations
|
16
|
+
|
17
|
+
def self.param(name, options={}, &block)
|
18
|
+
attribute(name, options, &block)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build_validation_class(&block)
|
23
|
+
kls = Class.new(self)
|
24
|
+
kls.class_eval(&block)
|
25
|
+
kls
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(attributes)
|
29
|
+
@raw_attributes = attributes
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
@attributes.get(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_h
|
38
|
+
super.select do |key, _|
|
39
|
+
@raw_attributes.to_h.has_key?(key.to_s) || @raw_attributes.to_h.has_key?(key.to_sym)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
klass.instance_exec(&block)
|
44
|
+
|
45
|
+
instance_variable_set('@params_attributes', klass)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_params_attributes_klass
|
49
|
+
instance_variable_get('@params_attributes')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_accessor :operation, :current_user
|
54
|
+
|
55
|
+
def initialize(operation, extra_params)
|
56
|
+
self.extra_params = extra_params || {}
|
57
|
+
self.operation = operation
|
58
|
+
end
|
59
|
+
|
60
|
+
def run
|
61
|
+
internal_call(nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
def internal_call(_)
|
65
|
+
serialize_with(action)
|
66
|
+
end
|
67
|
+
|
68
|
+
def params
|
69
|
+
@params ||= begin
|
70
|
+
if operation['value'].respond_to?(:to_h) && self.class.get_params_attributes_klass
|
71
|
+
self.class.get_params_attributes_klass.new(extra_params.merge({data: operation['value'].to_h}))
|
72
|
+
else
|
73
|
+
extra_params.merge({data: operation['value']})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def serialize_with(data)
|
79
|
+
self.class.serializer.new({collections: data}).serialize(context: self)
|
80
|
+
end
|
81
|
+
|
82
|
+
def request_path
|
83
|
+
@request_path ||= operation['path']
|
84
|
+
end
|
85
|
+
|
86
|
+
def request_path_with_query
|
87
|
+
request_path
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
attr_accessor :extra_params
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Result
|
3
|
+
attr_reader :object, :errors, :data
|
4
|
+
|
5
|
+
def initialize(object=nil, errors=nil, data={})
|
6
|
+
errors ||= Quiver::ErrorCollection.new
|
7
|
+
|
8
|
+
if block_given?
|
9
|
+
object = yield errors
|
10
|
+
end
|
11
|
+
|
12
|
+
self.object = object
|
13
|
+
self.errors = errors
|
14
|
+
self.data = data
|
15
|
+
end
|
16
|
+
|
17
|
+
def success?
|
18
|
+
errors.success?
|
19
|
+
end
|
20
|
+
|
21
|
+
def when(success: nil, failure: nil)
|
22
|
+
# one day if we ever have Ruby 2.1 we can delete this
|
23
|
+
raise ArgumentError, 'missing keyword: success' if success == nil
|
24
|
+
raise ArgumentError, 'missing keyword: failure' if failure == nil
|
25
|
+
|
26
|
+
if success?
|
27
|
+
success.call(object, self)
|
28
|
+
else
|
29
|
+
failure.call(errors, self)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
other.is_a?(Result) &&
|
35
|
+
object == other.object &&
|
36
|
+
errors == other.errors &&
|
37
|
+
data == other.data
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_writer :object, :errors, :data
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Quiver
|
2
|
+
module Router
|
3
|
+
def self.included(host)
|
4
|
+
host.send(:extend, ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def routes(&block)
|
9
|
+
@routes_block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def routes_block
|
13
|
+
@routes_block || Proc.new {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
resolver = Lotus::Routing::EndpointResolver.new(pattern: %Q|#{root_module}::Endpoints::%{controller}::%{action}|)
|
19
|
+
self.router = Lotus::Router.new(resolver: resolver, parsers: [JsonParser.new], &self.class.routes_block)
|
20
|
+
router.get('/', to: ->(env) { [200, {}, ["#{root_module} is now flying out of the Quiver!"]] })
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(env)
|
24
|
+
router.call(env)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_accessor :router
|
30
|
+
|
31
|
+
def root_module
|
32
|
+
self.class.parents[1].name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'quiver/route_helper'
|