croods 0.1.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +19 -0
  5. data/lib/croods.rb +58 -0
  6. data/lib/croods/action.rb +17 -0
  7. data/lib/croods/api.rb +26 -0
  8. data/lib/croods/api/initial_schema.json +52 -0
  9. data/lib/croods/attribute.rb +15 -0
  10. data/lib/croods/controller.rb +28 -0
  11. data/lib/croods/controller/actions.rb +71 -0
  12. data/lib/croods/controller/already_taken.rb +27 -0
  13. data/lib/croods/controller/authentication.rb +22 -0
  14. data/lib/croods/controller/authorization.rb +31 -0
  15. data/lib/croods/controller/collection.rb +15 -0
  16. data/lib/croods/controller/member.rb +45 -0
  17. data/lib/croods/controller/model.rb +17 -0
  18. data/lib/croods/controller/multi_tenancy.rb +54 -0
  19. data/lib/croods/controller/not_found.rb +22 -0
  20. data/lib/croods/controller/record_invalid.rb +22 -0
  21. data/lib/croods/controller/resource.rb +30 -0
  22. data/lib/croods/middleware.rb +44 -0
  23. data/lib/croods/middleware/base.rb +20 -0
  24. data/lib/croods/middleware/request_validation.rb +10 -0
  25. data/lib/croods/middleware/response_validation.rb +10 -0
  26. data/lib/croods/model.rb +41 -0
  27. data/lib/croods/policy.rb +118 -0
  28. data/lib/croods/policy/scope.rb +131 -0
  29. data/lib/croods/railtie.rb +6 -0
  30. data/lib/croods/resource.rb +39 -0
  31. data/lib/croods/resource/actions.rb +51 -0
  32. data/lib/croods/resource/attributes.rb +53 -0
  33. data/lib/croods/resource/attributes/base.rb +30 -0
  34. data/lib/croods/resource/attributes/request.rb +29 -0
  35. data/lib/croods/resource/attributes/response.rb +13 -0
  36. data/lib/croods/resource/authentication.rb +46 -0
  37. data/lib/croods/resource/authorization.rb +47 -0
  38. data/lib/croods/resource/controller.rb +50 -0
  39. data/lib/croods/resource/filters.rb +32 -0
  40. data/lib/croods/resource/identifier.rb +13 -0
  41. data/lib/croods/resource/json_schema.rb +32 -0
  42. data/lib/croods/resource/json_schema/definition.rb +34 -0
  43. data/lib/croods/resource/json_schema/definitions.rb +31 -0
  44. data/lib/croods/resource/json_schema/initial_schema.json +8 -0
  45. data/lib/croods/resource/json_schema/links.rb +40 -0
  46. data/lib/croods/resource/json_schema/links/collection.rb +70 -0
  47. data/lib/croods/resource/json_schema/links/create.rb +41 -0
  48. data/lib/croods/resource/json_schema/links/destroy.rb +40 -0
  49. data/lib/croods/resource/json_schema/links/index.rb +17 -0
  50. data/lib/croods/resource/json_schema/links/member.rb +40 -0
  51. data/lib/croods/resource/json_schema/links/show.rb +40 -0
  52. data/lib/croods/resource/json_schema/links/update.rb +40 -0
  53. data/lib/croods/resource/json_schema/properties.rb +32 -0
  54. data/lib/croods/resource/json_schema/required.rb +34 -0
  55. data/lib/croods/resource/model.rb +35 -0
  56. data/lib/croods/resource/names.rb +23 -0
  57. data/lib/croods/resource/paths.rb +16 -0
  58. data/lib/croods/resource/policy.rb +51 -0
  59. data/lib/croods/resource/services.rb +18 -0
  60. data/lib/croods/resource/sorting.rb +13 -0
  61. data/lib/croods/routes.rb +46 -0
  62. data/lib/croods/service.rb +28 -0
  63. data/lib/croods/version.rb +5 -0
  64. data/lib/tasks/croods_tasks.rake +4 -0
  65. metadata +316 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module Controller
6
+ def extend_controller(&block)
7
+ return unless block
8
+
9
+ controller_blocks << block
10
+ end
11
+
12
+ def controller_blocks
13
+ @controller_blocks ||= []
14
+ end
15
+
16
+ def controller_name
17
+ "#{namespace}Controller"
18
+ end
19
+
20
+ def controller
21
+ controller_name.constantize
22
+ end
23
+
24
+ def create_controller!
25
+ Object.const_set(controller_name, Class.new(ApplicationController))
26
+
27
+ controller_blocks.each do |block|
28
+ controller.instance_eval(&block)
29
+ end
30
+
31
+ create_actions!
32
+ create_additional_actions!
33
+ end
34
+
35
+ def create_actions!
36
+ actions.each do |action|
37
+ controller.define_method(
38
+ action.name, Croods::Controller::Actions.send(action.name)
39
+ )
40
+ end
41
+ end
42
+
43
+ def create_additional_actions!
44
+ additional_actions.each do |action|
45
+ controller.define_method(action.name, &action.block)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module Filters
6
+ def filter_by(name, optional: nil)
7
+ filters << Croods::Attribute.new(
8
+ name, :string, null: optional
9
+ )
10
+ end
11
+
12
+ def filters
13
+ @filters ||= []
14
+ end
15
+
16
+ def apply_filters(scope, params)
17
+ filters.each do |attribute|
18
+ unless model.has_attribute?(attribute.name)
19
+ attribute.name = "#{attribute.name}_id"
20
+ end
21
+
22
+ value = params[attribute.name]
23
+ next unless value
24
+
25
+ scope = scope.where(attribute.name => value)
26
+ end
27
+
28
+ scope
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module Identifier
6
+ def identifier(attribute = nil)
7
+ return @identifier ||= :id unless attribute
8
+
9
+ @identifier = attribute
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'json_schema/definition'
4
+ require_relative 'json_schema/definitions'
5
+ require_relative 'json_schema/properties'
6
+ require_relative 'json_schema/required'
7
+ require_relative 'json_schema/links'
8
+
9
+ module Croods
10
+ module Resource
11
+ module JsonSchema
12
+ def json_schema
13
+ return @json_schema if @json_schema
14
+
15
+ path = File.expand_path('json_schema/initial_schema.json', __dir__)
16
+ @json_schema = JSON.parse(File.read(path))
17
+ @json_schema['definitions'] = Definitions.schema(self)
18
+ @json_schema['properties'] = Properties.schema(self)
19
+ @json_schema['required'] = Required.schema(self)
20
+ @json_schema['links'] = Links.schema(self)
21
+ @json_schema
22
+ end
23
+
24
+ def ref(attribute = nil)
25
+ {
26
+ '$ref': "#/definitions/#{resource_name}" +
27
+ (attribute ? "/definitions/#{attribute}" : '')
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Definition
7
+ TYPES = { datetime: 'string', text: 'string', json: 'object' }.freeze
8
+
9
+ class << self
10
+ def schema(attribute)
11
+ { type: types(attribute) }.merge(format(attribute))
12
+ end
13
+
14
+ def format(attribute)
15
+ return {} unless attribute.type == :datetime
16
+
17
+ { format: 'date-time' }
18
+ end
19
+
20
+ def types(attribute)
21
+ types = []
22
+ types << type(attribute.type)
23
+ types << 'null' if attribute.null
24
+ types
25
+ end
26
+
27
+ def type(type)
28
+ TYPES[type] || type.to_s
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Definitions
7
+ TYPES = { datetime: 'string', text: 'string', json: 'object' }.freeze
8
+
9
+ class << self
10
+ def schema(resource)
11
+ attributes(resource).merge(identity: identity(resource))
12
+ end
13
+
14
+ def attributes(resource)
15
+ attributes = {}
16
+
17
+ resource.definitions.each_value do |attribute|
18
+ attributes[attribute.name] = Definition.schema(attribute)
19
+ end
20
+
21
+ attributes
22
+ end
23
+
24
+ def identity(resource)
25
+ resource.ref(resource.identifier)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/hyper-schema",
3
+ "stability": "prototype",
4
+ "type": [
5
+ "object"
6
+ ],
7
+ "additionalProperties": false
8
+ }
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'links/collection'
4
+ require_relative 'links/member'
5
+ require_relative 'links/index'
6
+ require_relative 'links/create'
7
+ require_relative 'links/update'
8
+ require_relative 'links/destroy'
9
+ require_relative 'links/show'
10
+
11
+ module Croods
12
+ module Resource
13
+ module JsonSchema
14
+ module Links
15
+ class << self
16
+ def schema(resource)
17
+ additional_actions(resource) + actions(resource)
18
+ end
19
+
20
+ def actions(resource)
21
+ resource.actions.map do |action|
22
+ action_module(action.name).link(resource)
23
+ end
24
+ end
25
+
26
+ def additional_actions(resource)
27
+ resource.additional_actions.map do |action|
28
+ action_module(action.on).link(resource, action)
29
+ end
30
+ end
31
+
32
+ def action_module(name)
33
+ "Croods::Resource::JsonSchema::Links::#{name.to_s.titleize}"
34
+ .constantize
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Links
7
+ module Collection
8
+ class << self
9
+ def link(resource, action = nil)
10
+ href = resource.collection_path
11
+ href += "/#{action.name}" if action
12
+
13
+ {
14
+ href: href,
15
+ method: action&.method&.upcase || 'GET',
16
+ rel: 'instances',
17
+ schema: schema(resource),
18
+ targetSchema: target_schema(resource)
19
+ }
20
+ end
21
+
22
+ def filters(resource)
23
+ filters = {}
24
+
25
+ resource.filters.each do |attribute|
26
+ unless resource.model.has_attribute?(attribute.name)
27
+ attribute.name = "#{attribute.name}_id"
28
+ end
29
+
30
+ filters[attribute.name] = definition(attribute)
31
+ end
32
+
33
+ filters
34
+ end
35
+
36
+ def definition(attribute)
37
+ definition = Definition.schema(attribute)
38
+
39
+ {
40
+ anyOf: [{ type: ['array'], items: definition }, definition]
41
+ }
42
+ end
43
+
44
+ def required(resource)
45
+ resource.filters.reject(&:null).map(&:name)
46
+ end
47
+
48
+ def schema(resource)
49
+ {
50
+ additionalProperties: false,
51
+ properties: filters(resource),
52
+ required: required(resource),
53
+ type: ['object']
54
+ }
55
+ end
56
+
57
+ def target_schema(resource)
58
+ {
59
+ anyOf: [
60
+ { type: ['array'], items: resource.ref },
61
+ { '$ref': '#/definitions/error' }
62
+ ]
63
+ }
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Links
7
+ module Create
8
+ class << self
9
+ def link(resource)
10
+ {
11
+ href: resource.collection_path,
12
+ method: 'POST',
13
+ rel: 'new',
14
+ schema: schema(resource),
15
+ targetSchema: target_schema(resource)
16
+ }
17
+ end
18
+
19
+ def schema(resource)
20
+ {
21
+ additionalProperties: false,
22
+ properties: Properties.schema(resource, request: true),
23
+ required: Required.schema(resource, request: true),
24
+ type: ['object']
25
+ }
26
+ end
27
+
28
+ def target_schema(resource)
29
+ {
30
+ anyOf: [
31
+ resource.ref,
32
+ { '$ref': '#/definitions/error' }
33
+ ]
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Links
7
+ module Destroy
8
+ class << self
9
+ def link(resource)
10
+ {
11
+ href: resource.member_path,
12
+ method: 'DELETE',
13
+ rel: 'self',
14
+ schema: schema,
15
+ targetSchema: target_schema(resource)
16
+ }
17
+ end
18
+
19
+ def schema
20
+ {
21
+ strictProperties: true,
22
+ properties: {},
23
+ type: ['object']
24
+ }
25
+ end
26
+
27
+ def target_schema(resource)
28
+ {
29
+ anyOf: [
30
+ resource.ref,
31
+ { '$ref': '#/definitions/error' }
32
+ ]
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Links
7
+ module Index
8
+ class << self
9
+ def link(resource)
10
+ Collection.link(resource)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Croods
4
+ module Resource
5
+ module JsonSchema
6
+ module Links
7
+ module Member
8
+ class << self
9
+ def link(resource, action)
10
+ {
11
+ href: "#{resource.member_path}/#{action.name}",
12
+ method: action.method.upcase,
13
+ rel: 'self',
14
+ schema: schema,
15
+ targetSchema: target_schema(resource)
16
+ }
17
+ end
18
+
19
+ def schema
20
+ {
21
+ strictProperties: true,
22
+ properties: {},
23
+ type: ['object']
24
+ }
25
+ end
26
+
27
+ def target_schema(resource)
28
+ {
29
+ anyOf: [
30
+ resource.ref,
31
+ { '$ref': '#/definitions/error' }
32
+ ]
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end