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,6 @@
1
+ module Quiver
2
+ module Serialization
3
+ end
4
+ end
5
+
6
+ require 'quiver/serialization/json_api'
@@ -0,0 +1,7 @@
1
+ module Quiver::Serialization
2
+ module JsonApi
3
+ end
4
+ end
5
+
6
+ require 'quiver/serialization/json_api/item_type_handler'
7
+ require 'quiver/serialization/json_api/serializer'
@@ -0,0 +1,96 @@
1
+ module Quiver::Serialization::JsonApi
2
+ class NoIdError < StandardError
3
+ end
4
+
5
+ class ItemTypeHandler
6
+ attr_accessor :type
7
+
8
+ def initialize(type, config_block, no_id=false)
9
+
10
+ self.attributes_storage = {}
11
+ self.links = {}
12
+ self.type = type
13
+
14
+ instance_exec(&config_block)
15
+
16
+ raise NoIdError, "no :id in handler for type #{type}" unless no_id || attributes_storage[:id]
17
+ end
18
+
19
+ def link(name, opts={})
20
+ links[name] = opts
21
+ end
22
+
23
+ def attribute(name, opts={})
24
+ attributes_storage[name] = opts
25
+ end
26
+
27
+ def attributes(*names)
28
+ names.each do |name|
29
+ attribute(name)
30
+ end
31
+ end
32
+
33
+ def calculated_attribute(name, &block)
34
+ attributes_storage[name] = {
35
+ proc: block
36
+ }
37
+ end
38
+
39
+ def serialize(item, opts={})
40
+ context = opts[:context]
41
+
42
+ serialized_links = links.each_with_object({}) do |(name, opts), h|
43
+ h[name] = {}
44
+
45
+ if href_proc = opts[:resource]
46
+ h[name][:resource] = context.instance_exec(item, &href_proc)
47
+ end
48
+
49
+ if self_proc = opts[:self]
50
+ h[name][:self] = context.instance_exec(item, &self_proc)
51
+ end
52
+
53
+ if value_proc = opts[:value]
54
+ value = value_from_proc(value_proc, item)
55
+ key = value.is_a?(Array) ? :ids : :id
56
+
57
+ h[name][key] = value
58
+ end
59
+
60
+ if type = opts[:type]
61
+ h[name][:type] = type
62
+ end
63
+
64
+ h[name].merge!(scope: context.instance_exec(item, &opts[:scope])) if opts[:scope]
65
+ end
66
+
67
+ serialized_attributes = attributes_storage.each_with_object({}) do |(name, opts), h|
68
+ if proc = opts[:proc]
69
+ h[name] = context.instance_exec(item, &proc)
70
+ elsif attr_alias = opts[:alias]
71
+ h[name] = item.send(attr_alias)
72
+ else
73
+ h[name] = item.send(name)
74
+ end
75
+ end
76
+
77
+ if serialized_links.count > 0
78
+ serialized_attributes.merge(links: serialized_links)
79
+ else
80
+ serialized_attributes
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ attr_accessor :attributes_storage, :links
87
+
88
+ def value_from_proc(potential_proc, item)
89
+ if potential_proc.respond_to?(:call)
90
+ potential_proc.call(item)
91
+ else
92
+ potential_proc
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,77 @@
1
+ module Quiver::Serialization
2
+ module JsonApi
3
+ module Serializer
4
+ def self.included(base)
5
+ base.send(:extend, ClassMethods)
6
+ end
7
+
8
+ attr_accessor :collection_info
9
+
10
+ def initialize(collection_info)
11
+ self.collection_info = collection_info
12
+ end
13
+
14
+ def serialize(opts={})
15
+ output = {}
16
+
17
+ [:data, :linked, :errors].each do |type|
18
+ if collection = fetch_collection(type)
19
+ output[type] = serialize_items(collection, opts)
20
+ end
21
+ end
22
+
23
+ output
24
+ end
25
+
26
+ private
27
+
28
+ def fetch_collection(key)
29
+ collection_info[:collections][key]
30
+ end
31
+
32
+ def serialize_items(items, opts)
33
+ items.map do |item|
34
+ serialization_type = if item.respond_to?(:serialization_type)
35
+ item.serialization_type
36
+ else
37
+ item.class.name
38
+ end
39
+
40
+ if handler = self.class.type_handlers[serialization_type]
41
+ handler.serialize(item, opts).merge(
42
+ type: serialization_type.underscore.pluralize
43
+ )
44
+ end
45
+ end.compact
46
+ end
47
+
48
+ module ClassMethods
49
+ def self.extended(base)
50
+ base.instance_variable_set('@type_handlers', {})
51
+
52
+ begin
53
+ base.for_type 'Error', JsonApi::ItemTypeHandler.new('Error', -> {
54
+ attributes :title, :detail, :path, :code
55
+ calculated_attribute(:status) { |item| item.status.to_s }
56
+ }, true)
57
+ rescue JsonApi::NoIdError
58
+ end
59
+ end
60
+
61
+ attr_accessor :type_handlers
62
+
63
+ def for_type(type, handler=nil, &block)
64
+ if handler
65
+ type_handlers[type] = handler
66
+ else
67
+ type_handlers[type] = JsonApi::ItemTypeHandler.new(type, block)
68
+ end
69
+ end
70
+
71
+ def type_handlers
72
+ instance_variable_get('@type_handlers')
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ module Quiver
2
+ module Tasks
3
+ def self.load_tasks
4
+ if parent::Application.using_active_record
5
+ include(::ActiveRecord::Tasks)
6
+ load_active_record_tasks
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def self.load_active_record_tasks
13
+ load 'active_record/railties/databases.rake'
14
+
15
+ database_tasks_constant = parent::Tasks::DatabaseTasks
16
+
17
+ ::ActiveRecord::Base.schema_format = :ruby #configuration.schema_format
18
+ database_tasks_constant.env = ENV['RACK_ENV'] #configuration.environment
19
+ database_tasks_constant.seed_loader = "Later" #configuration.seed_loader
20
+
21
+ database_configuration = YAML.load(ERB.new(File.read(File.join(parent::Application.app_root, 'config', 'database.yml'))).result)
22
+
23
+ ::ActiveRecord::Base.configurations = database_tasks_constant.database_configuration = database_configuration
24
+
25
+ database_tasks_constant.current_config = database_configuration[ENV['RACK_ENV']]
26
+ database_tasks_constant.db_dir = File.join(parent::Application.app_root, 'db')
27
+ database_tasks_constant.migrations_paths = File.join(parent::Application.app_root, 'db', 'migrate')
28
+ database_tasks_constant.root = parent::Application.app_root
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,83 @@
1
+ module Quiver
2
+ module Validators; end
3
+
4
+ require 'quiver/validators/base'
5
+ require 'quiver/validators/presence'
6
+ require 'quiver/validators/unique'
7
+
8
+ class Validator
9
+ VALIDATORS = {
10
+ presence: Quiver::Validators::Presence,
11
+ unique: Quiver::Validators::Unique
12
+ }.freeze
13
+
14
+ def initialize(definitions)
15
+ self.definitions = definitions
16
+ end
17
+
18
+ def validate(object, options={})
19
+ tags = options[:tags] || []
20
+
21
+ error_collections = definitions.map do |definition|
22
+ next unless tags_match?(tags, definition)
23
+
24
+ if definition[:options][:if]
25
+ next if !definition[:options][:if].call(object)
26
+ end
27
+
28
+ if definition[:options][:unless]
29
+ next if definition[:options][:unless].call(object)
30
+ end
31
+
32
+ attr_or_proc = definition[:attr_or_proc]
33
+
34
+ validator_options = definition[:options].select do |k, v|
35
+ !%i|except only if unless|.include?(k)
36
+ end
37
+
38
+ if attr_or_proc.is_a?(Proc)
39
+ result = attr_or_proc.call(object)
40
+
41
+ unless result.is_a?(Quiver::ErrorCollection)
42
+ raise TypeError, 'proc validators must return a Quiver::ErrorCollection'
43
+ end
44
+
45
+ result
46
+ else
47
+ value = object.public_send(attr_or_proc)
48
+
49
+ results = validator_options.map do |k, v|
50
+ validator_klass = VALIDATORS[k] || next
51
+ validator = validator_klass.new(
52
+ value, v, attr_or_proc, options[:mapper], options[:model]
53
+ )
54
+
55
+ validator.validate
56
+ end
57
+
58
+ results.compact.reduce(:+)
59
+ end
60
+ end
61
+
62
+ error_collections.compact.reduce(:+) || Quiver::ErrorCollection.new
63
+ end
64
+
65
+ private
66
+
67
+ attr_accessor :definitions
68
+
69
+ def tags_match?(tags, definition)
70
+ definition_options = definition[:options]
71
+
72
+ if definition_options.key?(:except)
73
+ return false if (definition_options[:except] & tags).any?
74
+ end
75
+
76
+ if definition_options.key?(:only)
77
+ return false unless definition_options[:only].any? { |t| tags.include?(t) }
78
+ end
79
+
80
+ return true
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,21 @@
1
+ module Quiver
2
+ module Validators
3
+ class Base
4
+ def initialize(value, options, name, mapper=nil, model=nil)
5
+ self.value = value
6
+ self.options = options
7
+ self.name = name
8
+ self.mapper = mapper
9
+ self.model = model
10
+ end
11
+
12
+ private
13
+
14
+ attr_accessor :value, :options, :name, :mapper, :model
15
+
16
+ def adapter
17
+ mapper.send(:adapter)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ module Quiver
2
+ module Validators
3
+ class Presence < Base
4
+ def validate
5
+ error_collection = Quiver::ErrorCollection.new
6
+
7
+ # if options is true
8
+ # then the value must be non-nil
9
+ # otherwise it must be nil
10
+ if options
11
+ if value == nil
12
+ error_collection << Quiver::Model::ValidationError.new(name, failed_presence_type)
13
+ end
14
+ else
15
+ if value != nil
16
+ error_collection << Quiver::Model::ValidationError.new(name, failed_non_presence_type)
17
+ end
18
+ end
19
+
20
+ error_collection
21
+ end
22
+
23
+ private
24
+
25
+ def failed_presence_type
26
+ "should_be_present"
27
+ end
28
+
29
+ def failed_non_presence_type
30
+ "should_not_be_present"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ require 'quiver/action/filter_value'
2
+
3
+ module Quiver
4
+ module Validators
5
+ class Unique < Base
6
+ def validate
7
+ errors = Quiver::ErrorCollection.new
8
+
9
+ result = mapper.send(:query,
10
+ filter: {
11
+ name => {'eq' => value}
12
+ }
13
+ )
14
+
15
+ if result.object.any?
16
+ unless result.object.size == 1 &&
17
+ result.object.first.send(adapter.class.primary_key_name) == model.send(adapter.class.primary_key_name)
18
+
19
+ errors << Quiver::Model::ValidationError.new(name, must_be_unique)
20
+ end
21
+ end
22
+
23
+ errors
24
+ end
25
+
26
+ private
27
+
28
+ def must_be_unique
29
+ 'must_be_unique'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Quiver
2
+ VERSION = "0.21.0"
3
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'quiver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "quiver"
8
+ spec.version = Quiver::VERSION
9
+ spec.authors = ["NCC Group Domain Services"]
10
+ spec.email = ["brady.love@nccgroup.trust", "emily.dobervich@nccgroup.trust"]
11
+ spec.licenses = ['MIT']
12
+
13
+ spec.summary = %q{Quiver is web API framework}
14
+ spec.description = %q{Quiver is a framework for writing web APIs}
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.executables << "quiver"
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "shotgun", "~> 0.9"
23
+ spec.add_dependency "thor", "~> 0.19"
24
+ spec.add_dependency "rack", "~> 1.5"
25
+ spec.add_dependency "activesupport"
26
+ spec.add_dependency "lotus-router", "0.3.0"
27
+ spec.add_dependency "lotus-controller", "0.4.0"
28
+ spec.add_dependency "pry"
29
+ spec.add_dependency "extant", "~> 0.3"
30
+ spec.add_dependency "rake", "~> 10.0"
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.9"
33
+ spec.add_development_dependency "rspec"
34
+ spec.add_development_dependency "rack-test"
35
+ spec.add_development_dependency "activerecord"
36
+ spec.add_development_dependency "delayed_job_active_record"
37
+ spec.add_development_dependency "sqlite3"
38
+ spec.add_development_dependency "factory_girl"
39
+ spec.add_development_dependency "ffaker"
40
+ spec.add_development_dependency "database_cleaner"
41
+ spec.add_development_dependency "timecop"
42
+ end