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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +6 -0
  6. data/README.md +22 -0
  7. data/Rakefile +17 -0
  8. data/bin/quiver +9 -0
  9. data/lib/quiver.rb +46 -0
  10. data/lib/quiver/abstract_action.rb +31 -0
  11. data/lib/quiver/action.rb +208 -0
  12. data/lib/quiver/action/filter_error.rb +33 -0
  13. data/lib/quiver/action/filter_value.rb +152 -0
  14. data/lib/quiver/action/invalid_request_body_error.rb +30 -0
  15. data/lib/quiver/action/pagination_link_builder.rb +67 -0
  16. data/lib/quiver/adapter.rb +51 -0
  17. data/lib/quiver/adapter/active_record_adapter_filter.rb +102 -0
  18. data/lib/quiver/adapter/active_record_helpers.rb +258 -0
  19. data/lib/quiver/adapter/arec_low_level_creator.rb +82 -0
  20. data/lib/quiver/adapter/arec_low_level_deleter.rb +74 -0
  21. data/lib/quiver/adapter/arec_low_level_updater.rb +105 -0
  22. data/lib/quiver/adapter/filter_helpers.rb +58 -0
  23. data/lib/quiver/adapter/helpers_helpers.rb +53 -0
  24. data/lib/quiver/adapter/memory_adapter_filter.rb +71 -0
  25. data/lib/quiver/adapter/memory_adapter_store.rb +34 -0
  26. data/lib/quiver/adapter/memory_helpers.rb +182 -0
  27. data/lib/quiver/adapter/memory_uuid_primary_key.rb +25 -0
  28. data/lib/quiver/application.rb +128 -0
  29. data/lib/quiver/cli/app.rb +25 -0
  30. data/lib/quiver/cli/generators/endpoint.rb +17 -0
  31. data/lib/quiver/cli/generators/new_application.rb +166 -0
  32. data/lib/quiver/cli/generators/new_application_cli.rb +25 -0
  33. data/lib/quiver/cli/server.rb +37 -0
  34. data/lib/quiver/cli/templates/Gemfile.tt +8 -0
  35. data/lib/quiver/cli/templates/Rakefile.tt +12 -0
  36. data/lib/quiver/cli/templates/config.tt +3 -0
  37. data/lib/quiver/cli/templates/config/database.tt +21 -0
  38. data/lib/quiver/cli/templates/gemspec.tt +33 -0
  39. data/lib/quiver/cli/templates/gitignore.tt +10 -0
  40. data/lib/quiver/cli/templates/lib/application.tt +14 -0
  41. data/lib/quiver/cli/templates/lib/application/config/router.tt +11 -0
  42. data/lib/quiver/cli/templates/lib/application/version.tt +3 -0
  43. data/lib/quiver/cli/templates/spec/spec_helper.tt +19 -0
  44. data/lib/quiver/duty.rb +34 -0
  45. data/lib/quiver/duty_master.rb +23 -0
  46. data/lib/quiver/duty_master/delayed_job_adapter.rb +15 -0
  47. data/lib/quiver/duty_master/memory_adapter.rb +18 -0
  48. data/lib/quiver/duty_test_helper.rb +23 -0
  49. data/lib/quiver/duty_test_helper/delayed_job_helper.rb +9 -0
  50. data/lib/quiver/duty_test_helper/memory_helper.rb +15 -0
  51. data/lib/quiver/error.rb +24 -0
  52. data/lib/quiver/error_collection.rb +60 -0
  53. data/lib/quiver/json_parser.rb +17 -0
  54. data/lib/quiver/logger.rb +26 -0
  55. data/lib/quiver/mapper.rb +311 -0
  56. data/lib/quiver/mapper/hook.rb +21 -0
  57. data/lib/quiver/mapper/mapper_result.rb +7 -0
  58. data/lib/quiver/mapper/not_found_error.rb +27 -0
  59. data/lib/quiver/mapper/simple_query_builder.rb +70 -0
  60. data/lib/quiver/mapper/soft_delete.rb +15 -0
  61. data/lib/quiver/mappers.rb +75 -0
  62. data/lib/quiver/middleware_stack.rb +35 -0
  63. data/lib/quiver/model.rb +63 -0
  64. data/lib/quiver/model/soft_delete.rb +14 -0
  65. data/lib/quiver/model/validation_error.rb +27 -0
  66. data/lib/quiver/model/validations.rb +45 -0
  67. data/lib/quiver/patcher.rb +94 -0
  68. data/lib/quiver/result.rb +44 -0
  69. data/lib/quiver/route_helper.rb +16 -0
  70. data/lib/quiver/router.rb +37 -0
  71. data/lib/quiver/serialization.rb +6 -0
  72. data/lib/quiver/serialization/json_api.rb +7 -0
  73. data/lib/quiver/serialization/json_api/item_type_handler.rb +96 -0
  74. data/lib/quiver/serialization/json_api/serializer.rb +77 -0
  75. data/lib/quiver/tasks.rb +31 -0
  76. data/lib/quiver/validator.rb +83 -0
  77. data/lib/quiver/validators/base.rb +21 -0
  78. data/lib/quiver/validators/presence.rb +34 -0
  79. data/lib/quiver/validators/unique.rb +33 -0
  80. data/lib/quiver/version.rb +3 -0
  81. data/quiver.gemspec +42 -0
  82. metadata +393 -0
@@ -0,0 +1,15 @@
1
+ module Quiver
2
+ module Mapper
3
+ module SoftDelete
4
+ def soft_delete(model)
5
+ model.deleted_at = Time.now
6
+ save(model)
7
+ end
8
+
9
+ def restore(model)
10
+ model.deleted_at = nil
11
+ save(model)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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
@@ -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,14 @@
1
+ module Quiver
2
+ module Model
3
+ module SoftDelete
4
+
5
+ def self.included(host)
6
+ host.send(:attribute, :deleted_at, Time)
7
+ end
8
+
9
+ def deleted?
10
+ !!deleted_at
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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,16 @@
1
+ module Quiver
2
+ class RouteHelper
3
+ def initialize(router)
4
+ self.router = router
5
+ end
6
+
7
+ def path(*args)
8
+ route = router.path(*args)
9
+ route.gsub(/%7B(.*?)%7D/, '{\1}')
10
+ end
11
+
12
+ private
13
+
14
+ attr_accessor :router
15
+ end
16
+ 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'