onsi 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed92bde9b8d7540e7d542621101fedc2cb414aaa07d24c751d57d7b4454b0bcb
4
- data.tar.gz: 441c0cd6376b4bcc6c086acb0cbec596fed03f0a190a8096e8dac639be4a28d9
3
+ metadata.gz: 11cdb10f50e5e342639f7957e3e2cf867decc2773065232c3a880983936a5122
4
+ data.tar.gz: f37f95aaddd5d3736a7f165b4362876ef7a7a60865bae5183b7b850ba4ad6746
5
5
  SHA512:
6
- metadata.gz: b1b31fa4f17aad3767d5f4f2b46bf92e52ff6bd9ec7343ab4e742796d41f6791f5944b07378ec3dfd16d11b2b1caca07608fea9ad3dcb911b1a89318d8be15cd
7
- data.tar.gz: 4e0e591bc4fea1df9aeedc73b6f558a12e7a41b42f65a780eff24784bad73c709731af923e1fd97d0346295ff911b66584cd11e74584071f6f0bffbbef176aef
6
+ metadata.gz: fce49cd88116a547bd44bdc835b5def7a2dc5d54b07360d0185bf99008ba66c02a7821dadcc77cf9909bc62549ce57af5d30fd27c1eed9068d67041bdd943638
7
+ data.tar.gz: 452661c725d56fc43dd03a16499321614063830a4cfc6d4af29fb122d6c8349e71f4d122d8d56637fa80eaee80bc89be3a36e55a7aa6b20415db83ffaae6e2e4
data/.circleci/config.yml CHANGED
@@ -16,6 +16,7 @@ jobs:
16
16
  command: |
17
17
  curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
18
18
  chmod +x ./cc-test-reporter
19
+ - run: gem install bundler
19
20
  - restore_cache:
20
21
  keys:
21
22
  - v1-gems-latest-{{ checksum "onsi.gemspec" }}
@@ -44,6 +45,7 @@ jobs:
44
45
  working_directory: ~/repo
45
46
  steps:
46
47
  - checkout
48
+ - run: gem install bundler
47
49
  - restore_cache:
48
50
  keys:
49
51
  - v1-gems-build_2_5-{{ checksum "onsi.gemspec" }}
@@ -65,6 +67,7 @@ jobs:
65
67
  working_directory: ~/repo
66
68
  steps:
67
69
  - checkout
70
+ - run: gem install bundler
68
71
  - restore_cache:
69
72
  keys:
70
73
  - v1-gems-build_2_4-{{ checksum "onsi.gemspec" }}
@@ -86,6 +89,7 @@ jobs:
86
89
  working_directory: ~/repo
87
90
  steps:
88
91
  - checkout
92
+ - run: gem install bundler
89
93
  - restore_cache:
90
94
  keys:
91
95
  - v1-gems-build_2_3-{{ checksum "onsi.gemspec" }}
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- onsi (0.3.0)
4
+ onsi (0.4.0)
5
5
  rails (>= 5.0, < 6.0)
6
6
 
7
7
  GEM
@@ -17,22 +17,11 @@ module Onsi
17
17
 
18
18
  def render_resource(resource, opts = {})
19
19
  version = opts.delete(:version) || self.class.render_version || Model::DEFAULT_API_VERSION
20
- payload = format_resource(resource, version)
20
+ payload = Resource.render(resource, version)
21
21
  render_options = {}
22
- render_options[:json] = { data: payload }
22
+ render_options[:json] = payload
23
23
  render_options.merge!(opts)
24
24
  render(render_options)
25
25
  end
26
-
27
- def format_resource(resource, version)
28
- case resource
29
- when Onsi::Resource
30
- resource
31
- when Enumerable
32
- resource.map { |res| format_resource(res, version) }
33
- else
34
- Onsi::Resource.new(resource, version)
35
- end
36
- end
37
26
  end
38
27
  end
@@ -0,0 +1,56 @@
1
+ module Onsi
2
+ class Includes
3
+ attr_reader :included
4
+
5
+ def initialize(included)
6
+ @included = parse_included(included)
7
+ end
8
+
9
+ def method_missing(name, *args, &block)
10
+ if name =~ /\Afetch_(.*)/
11
+ add_fetch_method(name.to_s.gsub(/\Afetch_/, ''), *args, &block)
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def load_included
18
+ @load_included ||= {}.tap do |root|
19
+ included.each do |name|
20
+ fetcher = fetch_methods[name]
21
+ next if fetcher.nil?
22
+ results = fetcher.call
23
+ root[name] = results
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_methods
31
+ @fetch_methods ||= {}
32
+ end
33
+
34
+ def add_fetch_method(name, &block)
35
+ if block.nil?
36
+ raise ArgumentError, "Must specify a block for fetch_#{name}"
37
+ end
38
+ fetch_methods[name.to_sym] = block
39
+ end
40
+
41
+ def parse_included(included)
42
+ case included
43
+ when Enumerable
44
+ included.map(&:to_sym)
45
+ when String
46
+ included.split(',').map(&:to_sym)
47
+ when Symbol
48
+ Array(included)
49
+ when nil
50
+ []
51
+ else
52
+ raise ArgumentError, "Onsi::Includes unknown included type #{included}"
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/onsi/params.rb CHANGED
@@ -72,8 +72,12 @@ module Onsi
72
72
  attributes.to_h.merge(relationships.to_h).with_indifferent_access
73
73
  end
74
74
 
75
+ def fetch(key, default = nil)
76
+ attrs_hash[key] || default
77
+ end
78
+
75
79
  def require(key)
76
- value = attributes.to_h.with_indifferent_access[key]
80
+ value = attrs_hash[key]
77
81
  if value.nil?
78
82
  raise MissingReqiredAttribute.new("Missing attribute #{key}", key)
79
83
  end
@@ -85,5 +89,11 @@ module Onsi
85
89
  rescue ActiveRecord::RecordNotFound
86
90
  raise RelationshipNotFound.new("Can't find relationship #{key}", key)
87
91
  end
92
+
93
+ private
94
+
95
+ def attrs_hash
96
+ @attrs_hash ||= attributes.to_h.with_indifferent_access
97
+ end
88
98
  end
89
99
  end
data/lib/onsi/resource.rb CHANGED
@@ -1,38 +1,107 @@
1
1
  module Onsi
2
2
  class Resource
3
- attr_reader :object, :version
3
+ attr_reader :object, :version, :includes
4
+
5
+ TYPE_KEY = 'type'.freeze
6
+ ID_KEY = 'id'.freeze
7
+ ATTRIBUTES_KEY = 'attributes'.freeze
8
+ RELATIONSHIPS_KEY = 'relationships'.freeze
9
+ META_KEY = 'meta'.freeze
10
+ DATA_KEY = 'data'.freeze
11
+ INCLUDED_KEY = 'included'.freeze
4
12
 
5
13
  class InvalidResourceError < StandardError; end
6
14
 
7
- def initialize(object, version = Model::DEFAULT_API_VERSION)
15
+ class << self
16
+ def as_resource(resource, version)
17
+ case resource
18
+ when Onsi::Resource
19
+ resource
20
+ when Enumerable
21
+ resource.map { |res| as_resource(res, version) }
22
+ else
23
+ Onsi::Resource.new(resource, version)
24
+ end
25
+ end
26
+
27
+ def render(resource, version)
28
+ resources = as_resource(resource, version)
29
+ {}.tap do |root|
30
+ root[DATA_KEY] = resources.as_json
31
+ included = all_included(resources)
32
+ if included.any?
33
+ root[INCLUDED_KEY] = included
34
+ end
35
+ end
36
+ end
37
+
38
+ def all_included(resources)
39
+ Array(resources).map(&:flat_includes).flatten.uniq do |res|
40
+ "#{res[TYPE_KEY]}-#{res[ID_KEY]}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def initialize(object, version = nil, includes: nil)
8
46
  @object = object
9
- @version = version
47
+ @version = version || Model::DEFAULT_API_VERSION
48
+ @includes = includes
10
49
  validate!
11
50
  end
12
51
 
13
52
  def as_json(_opts = {})
14
53
  {}.tap do |root|
15
- root['type'] = type
16
- root['id'] = object.id.to_s
17
- root['attributes'] = generate_attributes
18
- rela = generate_relationships
19
- root['relationships'] = rela if rela.any?
20
- meta = generate_metadata
21
- root['meta'] = meta if meta.any?
54
+ root[TYPE_KEY] = type
55
+ root[ID_KEY] = object.id.to_s
56
+ root[ATTRIBUTES_KEY] = generate_attributes
57
+ append_relationships(root)
58
+ append_meta(root)
59
+ append_includes(root)
22
60
  end
23
61
  end
24
62
 
63
+ def rendered_includes
64
+ @rendered_includes ||= perform_render_includes
65
+ end
66
+
67
+ def flat_includes
68
+ rendered_includes.values.map { |root| root[DATA_KEY] }.flatten
69
+ end
70
+
25
71
  private
26
72
 
27
73
  def validate!
28
- return if object.class.included_modules.include?(Onsi::Model)
29
- raise InvalidResourceError, "Trying to render a #{object.class.name}. But it doesn't include Onsi::Model"
74
+ unless object.class.included_modules.include?(Onsi::Model)
75
+ raise InvalidResourceError, "Trying to render a #{object.class.name}. But it doesn't include Onsi::Model"
76
+ end
77
+ if includes.present? && !includes.is_a?(Onsi::Includes)
78
+ raise InvalidResourceError, "Included resources in #{self} is not a Onsi::Includes"
79
+ end
30
80
  end
31
81
 
32
82
  def type
33
83
  object.class.api_renderer(version, for_render: true).type || object.class.name.underscore
34
84
  end
35
85
 
86
+ def append_relationships(root)
87
+ relationships = generate_relationships
88
+ return unless relationships.any?
89
+ root[RELATIONSHIPS_KEY] = relationships
90
+ end
91
+
92
+ def append_meta(root)
93
+ meta = generate_metadata
94
+ return unless meta.any?
95
+ root[META_KEY] = meta
96
+ end
97
+
98
+ def append_includes(root)
99
+ includes = generate_includes
100
+ return unless includes.any?
101
+ root[RELATIONSHIPS_KEY] ||= {}
102
+ root[RELATIONSHIPS_KEY].merge!(includes)
103
+ end
104
+
36
105
  def generate_attributes
37
106
  object.class.api_renderer(version, for_render: true).render_attributes(object)
38
107
  end
@@ -44,5 +113,40 @@ module Onsi
44
113
  def generate_metadata
45
114
  object.class.api_renderer(version, for_render: true).render_metadata(object)
46
115
  end
116
+
117
+ def generate_includes
118
+ {}.tap do |root|
119
+ rendered_includes.each do |key, values|
120
+ included = values[DATA_KEY]
121
+ case included
122
+ when Array
123
+ root[key] = {}.tap do |subobj|
124
+ subobj[DATA_KEY] = included.map do |inc|
125
+ {
126
+ TYPE_KEY => inc[TYPE_KEY],
127
+ ID_KEY => inc[ID_KEY]
128
+ }
129
+ end
130
+ end
131
+ when Hash
132
+ root[key] = { DATA_KEY => { TYPE_KEY => included[TYPE_KEY], ID_KEY => included[ID_KEY] } }
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def perform_render_includes
139
+ included = includes&.load_included
140
+ {}.tap do |root|
141
+ Hash(included).each do |key, results|
142
+ root[key.to_s] = {}
143
+ root[key.to_s][DATA_KEY] = render_included(results)
144
+ end
145
+ end
146
+ end
147
+
148
+ def render_included(resources)
149
+ Resource.as_resource(resources, version).as_json
150
+ end
47
151
  end
48
152
  end
data/lib/onsi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Onsi
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/lib/onsi.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'onsi/controller'
2
2
  require 'onsi/error_responder'
3
3
  require 'onsi/errors'
4
+ require 'onsi/includes'
4
5
  require 'onsi/model'
5
6
  require 'onsi/params'
6
7
  require 'onsi/resource'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Skylar Schipper
@@ -166,6 +166,7 @@ files:
166
166
  - lib/onsi/controller.rb
167
167
  - lib/onsi/error_responder.rb
168
168
  - lib/onsi/errors.rb
169
+ - lib/onsi/includes.rb
169
170
  - lib/onsi/model.rb
170
171
  - lib/onsi/params.rb
171
172
  - lib/onsi/resource.rb