jsonapi-consumer 0.1.0.pre.1
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/CHANGELOG.md +12 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +176 -0
- data/README.md +37 -0
- data/Rakefile +12 -0
- data/jsonapi-consumer.gemspec +32 -0
- data/lib/jsonapi/consumer/errors.rb +98 -0
- data/lib/jsonapi/consumer/middleware/parse_json.rb +32 -0
- data/lib/jsonapi/consumer/middleware/raise_error.rb +21 -0
- data/lib/jsonapi/consumer/middleware/request_timeout.rb +9 -0
- data/lib/jsonapi/consumer/middleware.rb +5 -0
- data/lib/jsonapi/consumer/parser.rb +75 -0
- data/lib/jsonapi/consumer/query/base.rb +34 -0
- data/lib/jsonapi/consumer/query/create.rb +9 -0
- data/lib/jsonapi/consumer/query/delete.rb +10 -0
- data/lib/jsonapi/consumer/query/find.rb +16 -0
- data/lib/jsonapi/consumer/query/new.rb +15 -0
- data/lib/jsonapi/consumer/query/update.rb +11 -0
- data/lib/jsonapi/consumer/query.rb +5 -0
- data/lib/jsonapi/consumer/resource/association_concern.rb +203 -0
- data/lib/jsonapi/consumer/resource/attributes_concern.rb +70 -0
- data/lib/jsonapi/consumer/resource/connection_concern.rb +94 -0
- data/lib/jsonapi/consumer/resource/finders_concern.rb +28 -0
- data/lib/jsonapi/consumer/resource/object_build_concern.rb +28 -0
- data/lib/jsonapi/consumer/resource/serializer_concern.rb +64 -0
- data/lib/jsonapi/consumer/resource.rb +88 -0
- data/lib/jsonapi/consumer/version.rb +5 -0
- data/lib/jsonapi/consumer.rb +40 -0
- data/spec/fixtures/.gitkeep +0 -0
- data/spec/fixtures/resources.rb +33 -0
- data/spec/fixtures/responses.rb +51 -0
- data/spec/jsonapi/consumer/associations_spec.rb +141 -0
- data/spec/jsonapi/consumer/attributes_spec.rb +27 -0
- data/spec/jsonapi/consumer/connection_spec.rb +101 -0
- data/spec/jsonapi/consumer/error_handling_spec.rb +37 -0
- data/spec/jsonapi/consumer/object_build_spec.rb +20 -0
- data/spec/jsonapi/consumer/parser_spec.rb +41 -0
- data/spec/jsonapi/consumer/resource_spec.rb +62 -0
- data/spec/jsonapi/consumer/serializer_spec.rb +41 -0
- data/spec/spec_helper.rb +97 -0
- data/spec/support/.gitkeep +0 -0
- data/spec/support/load_fixtures.rb +4 -0
- metadata +242 -0
@@ -0,0 +1,203 @@
|
|
1
|
+
module JSONAPI::Consumer::Resource
|
2
|
+
class MisconfiguredAssociation < StandardError; end
|
3
|
+
|
4
|
+
module AssociationConcern
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
attr_writer :_associations
|
9
|
+
|
10
|
+
# Defines a has many relationship.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# class User
|
14
|
+
# include JSONAPI::Consumer::Resource
|
15
|
+
# has_many :articles, class_name: 'Article'
|
16
|
+
# end
|
17
|
+
def has_many(*attrs)
|
18
|
+
associate(:has_many, attrs)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @todo belongs to is not supported yet.
|
22
|
+
#
|
23
|
+
def belongs_to(*attrs)
|
24
|
+
associate(:belongs_to, attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Defines a single relationship.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# class Article
|
31
|
+
# include JSONAPI::Consumer::Resource
|
32
|
+
# has_one :user, class_name: 'User'
|
33
|
+
# end
|
34
|
+
def has_one(*attrs)
|
35
|
+
associate(:has_one, attrs)
|
36
|
+
end
|
37
|
+
|
38
|
+
# :nodoc:
|
39
|
+
def _associations
|
40
|
+
@_associations ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# :nodoc:
|
44
|
+
def _association_for(name)
|
45
|
+
_associations[name.to_sym]
|
46
|
+
end
|
47
|
+
|
48
|
+
# :nodoc:
|
49
|
+
def _association_type(name)
|
50
|
+
_association_for(name).fetch(:type)
|
51
|
+
end
|
52
|
+
|
53
|
+
# :nodoc:
|
54
|
+
def _association_class_name(name)
|
55
|
+
if class_name = _association_for(name).fetch(:class_name)
|
56
|
+
begin
|
57
|
+
class_name.constantize
|
58
|
+
rescue NameError
|
59
|
+
raise MisconfiguredAssociation,
|
60
|
+
"#{self}##{_association_type(name)} #{name} has a class_name specified that does not exist."
|
61
|
+
end
|
62
|
+
else
|
63
|
+
raise MisconfiguredAssociation,
|
64
|
+
"#{self}##{_association_type(name)} #{name} is missing an explicit `:class_name` value."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# :nodoc:
|
69
|
+
def associate(type, attrs)
|
70
|
+
options = attrs.extract_options!
|
71
|
+
|
72
|
+
self._associations = _associations.dup
|
73
|
+
|
74
|
+
attrs.each do |attr|
|
75
|
+
unless method_defined?(attr)
|
76
|
+
define_method attr do
|
77
|
+
read_association(attr)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if type == :has_many
|
82
|
+
unless method_defined?(:"#{attr.to_s.singularize}_ids")
|
83
|
+
define_method :"#{attr.to_s.singularize}_ids" do
|
84
|
+
if objs = read_association(attr)
|
85
|
+
objs.collect {|o| o.send(o.primary_key)}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
unless method_defined?(:"#{attr.to_s.singularize}_id")
|
91
|
+
define_method :"#{attr.to_s.singularize}_id" do
|
92
|
+
if obj = read_association(attr)
|
93
|
+
obj.send(obj.primary_key)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
unless method_defined?(:"#{attr}=")
|
99
|
+
define_method :"#{attr}=" do |val|
|
100
|
+
val = [val].flatten if type == :has_many && !val.nil?
|
101
|
+
set_association(attr, val)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
self._associations[attr] = {type: type, class_name: options.delete(:class_name), options: options}
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# :nodoc:
|
111
|
+
def each_association(&block)
|
112
|
+
self.class._associations.dup.each do |name, options|
|
113
|
+
association = self.send(name)
|
114
|
+
|
115
|
+
if block_given?
|
116
|
+
block.call(name, association, options[:options])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Helper method that returns the names of defined associations.
|
122
|
+
#
|
123
|
+
# @return [Array<Symbol>] a list of association names
|
124
|
+
def association_names
|
125
|
+
self.class._associations.keys
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
|
131
|
+
# Read the specified association.
|
132
|
+
#
|
133
|
+
# @param name [Symbol, String] the association name, `:users` or `:author`
|
134
|
+
#
|
135
|
+
# @return [Array, Object, nil] the value(s) of that association.
|
136
|
+
def read_association(name)
|
137
|
+
type = _association_type(name)
|
138
|
+
_associations.fetch(name, nil)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Set values for the key'd association.
|
142
|
+
#
|
143
|
+
# @param key [Symbol] the association name, `:users` or `:author`
|
144
|
+
# @param value the value to set on the specified association
|
145
|
+
def set_association(key, value)
|
146
|
+
_associations[key.to_sym] = _cast_association(key, value)
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# Helper method that verifies a given association exists.
|
151
|
+
#
|
152
|
+
# @param attr_name [String, Symbol] the association name
|
153
|
+
#
|
154
|
+
# @return [true, false]
|
155
|
+
def has_association?(attr_name)
|
156
|
+
_associations.has_key?(attr_name.to_sym)
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# :nodoc:
|
162
|
+
def _cast_association(name, value)
|
163
|
+
return if value.is_a?(Array) && _association_type(name) != :has_many
|
164
|
+
return value if value.nil?
|
165
|
+
|
166
|
+
association_class = _association_class_name(name)
|
167
|
+
|
168
|
+
case value
|
169
|
+
when association_class
|
170
|
+
value
|
171
|
+
when Array
|
172
|
+
value.collect {|i| _cast_association(name, i) }
|
173
|
+
when Hash
|
174
|
+
association_class.new(value)
|
175
|
+
when NilClass
|
176
|
+
nil
|
177
|
+
else
|
178
|
+
association_class.new({association_class.primary_key => value})
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# :nodoc:
|
183
|
+
def _association_for(name)
|
184
|
+
self.class._association_for(name)
|
185
|
+
end
|
186
|
+
|
187
|
+
# :nodoc:
|
188
|
+
def _association_type(name)
|
189
|
+
self.class._association_type(name)
|
190
|
+
end
|
191
|
+
|
192
|
+
# :nodoc:
|
193
|
+
def _association_class_name(name)
|
194
|
+
self.class._association_class_name(name)
|
195
|
+
end
|
196
|
+
|
197
|
+
# :nodoc:
|
198
|
+
def _associations
|
199
|
+
@associations ||= {}
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module JSONAPI::Consumer::Resource
|
2
|
+
module AttributesConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include ActiveModel::AttributeMethods
|
6
|
+
include ActiveModel::Dirty
|
7
|
+
|
8
|
+
included do
|
9
|
+
attr_reader :attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def attributes=(attrs={})
|
13
|
+
@attributes ||= {}
|
14
|
+
|
15
|
+
return @attributes unless attrs.present?
|
16
|
+
attrs.each do |key, value|
|
17
|
+
set_attribute(key, value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def update_attributes(attrs={})
|
22
|
+
self.attributes = attrs
|
23
|
+
# FIXME save
|
24
|
+
end
|
25
|
+
|
26
|
+
def persisted?
|
27
|
+
!self.to_param.blank?
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_param
|
31
|
+
attributes.fetch(primary_key, '').to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# def [](key)
|
35
|
+
# read_attribute(key)
|
36
|
+
# end
|
37
|
+
|
38
|
+
# def []=(key, value)
|
39
|
+
# set_attribute(key, value)
|
40
|
+
# end
|
41
|
+
|
42
|
+
alias :respond_to_without_attributes? :respond_to?
|
43
|
+
def respond_to?(method, include_private_methods=false)
|
44
|
+
if super
|
45
|
+
true
|
46
|
+
elsif !include_private_methods && super(method, true)
|
47
|
+
# If we're here then we haven't found among non-private methods
|
48
|
+
# but found among all methods. Which means that the given method is private.
|
49
|
+
false
|
50
|
+
else
|
51
|
+
has_attribute?(method)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def read_attribute(name)
|
58
|
+
attributes.fetch(name, nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_attribute(key, value)
|
62
|
+
attributes[key.to_sym] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
def has_attribute?(attr_name)
|
66
|
+
attributes.has_key?(attr_name.to_sym)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module JSONAPI::Consumer
|
2
|
+
module ConnectionConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def parser_class
|
7
|
+
@parser ||= Parser
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(response)
|
11
|
+
parser = parser_class.new(self, response)
|
12
|
+
|
13
|
+
if response.status && response.status == 204
|
14
|
+
true
|
15
|
+
else
|
16
|
+
parser.build
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# :nodoc:
|
21
|
+
def _run_request(query_object)
|
22
|
+
parse(_connection.send(query_object.request_method, query_object.path, query_object.params, query_object.headers))
|
23
|
+
end
|
24
|
+
|
25
|
+
# :nodoc:
|
26
|
+
def _connection
|
27
|
+
@connection ||= begin
|
28
|
+
Faraday.new(url: self.host, ssl: self.ssl) do |conn|
|
29
|
+
conn.request :json
|
30
|
+
|
31
|
+
conn.use Middleware::RequestTimeout
|
32
|
+
conn.use Middleware::ParseJson
|
33
|
+
|
34
|
+
conn.use Middleware::RaiseError
|
35
|
+
conn.adapter Faraday.default_adapter
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_valid?
|
42
|
+
errors.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def save
|
46
|
+
query = persisted? ?
|
47
|
+
Query::Update.new(self.class, self.serializable_hash) :
|
48
|
+
Query::Create.new(self.class, self.serializable_hash)
|
49
|
+
|
50
|
+
results = run_request(query)
|
51
|
+
|
52
|
+
if self.errors.empty?
|
53
|
+
self.attributes = results.first.attributes
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy
|
61
|
+
if run_request(Query::Delete.new(self.class, self.serializable_hash))
|
62
|
+
self.attributes.clear
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# :nodoc:
|
72
|
+
def run_request(*args)
|
73
|
+
begin
|
74
|
+
self.errors.clear
|
75
|
+
request = self.class._run_request(*args)
|
76
|
+
rescue JSONAPI::Consumer::Errors::BadRequest => e
|
77
|
+
e.errors.map do |error|
|
78
|
+
process_error(error.dup)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# :nodoc:
|
84
|
+
def process_error(err)
|
85
|
+
field = err.fetch('path', '')
|
86
|
+
attr = field.match(/\A\/(\w+)\z/)
|
87
|
+
if attr[1] && has_attribute?(attr[1])
|
88
|
+
self.errors.add(attr[1].to_sym, err.fetch('detail', ''))
|
89
|
+
else
|
90
|
+
self.errors.add(:base, err.fetch('detail', ''))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module JSONAPI::Consumer::Resource
|
2
|
+
module FindersConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def all(options={})
|
7
|
+
_run_request(JSONAPI::Consumer::Query::Find.new(self, options))
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(options)
|
11
|
+
options = {self.primary_key => options} unless options.is_a?(Hash)
|
12
|
+
_run_request(JSONAPI::Consumer::Query::Find.new(self, options))
|
13
|
+
end
|
14
|
+
|
15
|
+
def primary_key
|
16
|
+
@primary_key ||= :id
|
17
|
+
end
|
18
|
+
|
19
|
+
def primary_key=(val)
|
20
|
+
@primary_key = val.to_sym
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def primary_key
|
25
|
+
self.class.primary_key
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module JSONAPI::Consumer::Resource
|
2
|
+
module ObjectBuildConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :request_new_object_on_build
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
# If class attribute `request_new_object_on_build`:
|
12
|
+
#
|
13
|
+
# True:
|
14
|
+
# will send a request to `{path}/new` to get an attributes list
|
15
|
+
#
|
16
|
+
# False:
|
17
|
+
# acts as an alias for `new`
|
18
|
+
#
|
19
|
+
def build
|
20
|
+
if !!self.request_new_object_on_build
|
21
|
+
_run_request(JSONAPI::Consumer::Query::New.new(self, {})).first
|
22
|
+
else
|
23
|
+
new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module JSONAPI::Consumer::Resource
|
2
|
+
module SerializerConcern
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def serializable_hash(options={})
|
6
|
+
@hash = persisted? ? attributes : attributes.except(self.class.primary_key)
|
7
|
+
|
8
|
+
self.each_association do |name, association, options|
|
9
|
+
@hash[:links] ||= {}
|
10
|
+
# unless options[:embed] == :ids
|
11
|
+
# @hash[:linked] ||= {}
|
12
|
+
# end
|
13
|
+
|
14
|
+
if association.respond_to?(:each)
|
15
|
+
add_links(name, association, options)
|
16
|
+
else
|
17
|
+
add_link(name, association, options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
@hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_links(name, association, options)
|
25
|
+
@hash[:links][name] ||= []
|
26
|
+
@hash[:links][name] += association.map do |obj|
|
27
|
+
case obj.class
|
28
|
+
when String, Integer
|
29
|
+
obj
|
30
|
+
else
|
31
|
+
obj.to_param
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# unless options[:embed] == :ids
|
36
|
+
# @hash[:linked][name] ||= []
|
37
|
+
# @hash[:linked][name] += association.map { |item| item.attributes(options) }
|
38
|
+
# end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_link(name, association, options)
|
42
|
+
return if association.nil?
|
43
|
+
|
44
|
+
@hash[:links][name] = case association.class
|
45
|
+
when String, Integer
|
46
|
+
association
|
47
|
+
else
|
48
|
+
association.to_param
|
49
|
+
end
|
50
|
+
|
51
|
+
# unless options[:embed] == :ids
|
52
|
+
# plural_name = name.to_s.pluralize.to_sym
|
53
|
+
|
54
|
+
# @hash[:linked][plural_name] ||= []
|
55
|
+
# @hash[:linked][plural_name].push association.attributes(options)
|
56
|
+
# end
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_json(options={})
|
60
|
+
serializable_hash(options).to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module JSONAPI::Consumer
|
2
|
+
module Resource
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
|
8
|
+
attr_reader :errors
|
9
|
+
class_attribute :host
|
10
|
+
end
|
11
|
+
|
12
|
+
include ObjectBuildConcern
|
13
|
+
include AttributesConcern
|
14
|
+
include AssociationConcern
|
15
|
+
include FindersConcern
|
16
|
+
include SerializerConcern
|
17
|
+
include ConnectionConcern
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def json_key
|
21
|
+
self.name.demodulize.pluralize.underscore
|
22
|
+
end
|
23
|
+
|
24
|
+
def host
|
25
|
+
@host || raise(NotImplementedError, 'host was not set')
|
26
|
+
end
|
27
|
+
|
28
|
+
def path
|
29
|
+
json_key
|
30
|
+
end
|
31
|
+
|
32
|
+
def ssl
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def human_attribute_name(attr, options = {})
|
39
|
+
attr
|
40
|
+
end
|
41
|
+
|
42
|
+
def lookup_ancestors
|
43
|
+
[self]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(params={})
|
48
|
+
(params || {}).slice(*association_names).each do |key, value|
|
49
|
+
send(:"#{key}=", value)
|
50
|
+
end
|
51
|
+
|
52
|
+
self.attributes = params.except(*association_names) if params
|
53
|
+
@errors = ActiveModel::Errors.new(self)
|
54
|
+
super()
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns an Enumerable of all key attributes if any is set, regardless
|
58
|
+
# if the object is persisted or not.
|
59
|
+
# Returns nil if there are no key attributes.
|
60
|
+
#
|
61
|
+
# (see ActiveModel::Conversion#to_key)
|
62
|
+
def to_key
|
63
|
+
to_param ? [to_param] : nil
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def read_attribute_for_validation(attr)
|
69
|
+
read_attribute(attr)
|
70
|
+
end
|
71
|
+
|
72
|
+
def method_missing(method, *args, &block)
|
73
|
+
if respond_to_without_attributes?(method, true)
|
74
|
+
super
|
75
|
+
else
|
76
|
+
if method.to_s =~ /^(.*)=$/
|
77
|
+
set_attribute($1, args.first)
|
78
|
+
elsif has_attribute?(method)
|
79
|
+
read_attribute(method)
|
80
|
+
elsif has_association?(method)
|
81
|
+
read_assocation(method)
|
82
|
+
else
|
83
|
+
super
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "jsonapi/consumer/version"
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
require 'active_model'
|
6
|
+
|
7
|
+
require "active_support/concern"
|
8
|
+
require "active_support/core_ext"
|
9
|
+
require "active_support/inflector"
|
10
|
+
|
11
|
+
module JSONAPI
|
12
|
+
module Consumer
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require "jsonapi/consumer/errors"
|
18
|
+
|
19
|
+
require "jsonapi/consumer/middleware"
|
20
|
+
require "jsonapi/consumer/middleware/parse_json"
|
21
|
+
require "jsonapi/consumer/middleware/raise_error"
|
22
|
+
require "jsonapi/consumer/middleware/request_timeout"
|
23
|
+
|
24
|
+
require "jsonapi/consumer/parser"
|
25
|
+
|
26
|
+
require "jsonapi/consumer/query"
|
27
|
+
require "jsonapi/consumer/query/base"
|
28
|
+
require "jsonapi/consumer/query/create"
|
29
|
+
require "jsonapi/consumer/query/delete"
|
30
|
+
require "jsonapi/consumer/query/find"
|
31
|
+
require "jsonapi/consumer/query/new"
|
32
|
+
require "jsonapi/consumer/query/update"
|
33
|
+
|
34
|
+
require "jsonapi/consumer/resource/association_concern"
|
35
|
+
require "jsonapi/consumer/resource/attributes_concern"
|
36
|
+
require "jsonapi/consumer/resource/connection_concern"
|
37
|
+
require "jsonapi/consumer/resource/finders_concern"
|
38
|
+
require "jsonapi/consumer/resource/object_build_concern"
|
39
|
+
require "jsonapi/consumer/resource/serializer_concern"
|
40
|
+
require "jsonapi/consumer/resource"
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Base
|
2
|
+
include JSONAPI::Consumer::Resource
|
3
|
+
|
4
|
+
self.host = 'http://localhost:3000/api/'
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class BasicResource < Base
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class BuildRequest < Base
|
13
|
+
self.request_new_object_on_build = true
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# BEGIN - Blog example
|
18
|
+
module Blog
|
19
|
+
class Author < Base
|
20
|
+
has_many :posts, class_name: 'Blog::Post'
|
21
|
+
end
|
22
|
+
|
23
|
+
class Post < Base
|
24
|
+
has_one :author, class_name: 'Blog::Author'
|
25
|
+
has_many :comments, class_name: 'Blog::Comment'
|
26
|
+
end
|
27
|
+
|
28
|
+
class Comment < Base
|
29
|
+
# belongs_to :post, class_name: 'Blog::Post'
|
30
|
+
# has_one :author, class_name: 'Blog::Author'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
# END - Blog example
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Responses
|
2
|
+
def self.sideload
|
3
|
+
{
|
4
|
+
posts: [
|
5
|
+
{
|
6
|
+
links: {
|
7
|
+
comments: [
|
8
|
+
"82083863-bba9-480e-a281-f5d34e7dc0ca",
|
9
|
+
"3b402e8a-7c35-4915-8c72-07ea7779ab76"
|
10
|
+
]
|
11
|
+
},
|
12
|
+
id: "e6d1b7ac-80d8-40dd-877d-f5bd40feabfb",
|
13
|
+
title: "Friday Post",
|
14
|
+
created_at: "2014-10-19T22:32:52.913Z",
|
15
|
+
updated_at: "2014-10-19T22:32:52.967Z"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
links: {
|
19
|
+
comments: [
|
20
|
+
"9c9ba83b-024c-4d4c-9573-9fd41b95fc14",
|
21
|
+
"27fcf6e8-24b0-41db-94b1-812046a10f54"
|
22
|
+
]
|
23
|
+
},
|
24
|
+
id: "ea006f14-6d05-4e87-bfe7-ee8ae3358840",
|
25
|
+
title: "Monday Post",
|
26
|
+
created_at: "2014-10-19T22:32:52.933Z",
|
27
|
+
updated_at: "2014-10-19T22:32:52.969Z"
|
28
|
+
}
|
29
|
+
],
|
30
|
+
linked: {
|
31
|
+
comments: [
|
32
|
+
{
|
33
|
+
id: "82083863-bba9-480e-a281-f5d34e7dc0ca",
|
34
|
+
content: "Awesome article",
|
35
|
+
created_at: "2014-10-19T22:32:52.933Z",
|
36
|
+
updated_at: "2014-10-19T22:32:52.969Z"
|
37
|
+
},
|
38
|
+
{
|
39
|
+
id: "3b402e8a-7c35-4915-8c72-07ea7779ab76",
|
40
|
+
content: "Hated it",
|
41
|
+
created_at: "2014-10-19T22:32:52.933Z",
|
42
|
+
updated_at: "2014-10-19T22:32:52.969Z"
|
43
|
+
}
|
44
|
+
]
|
45
|
+
},
|
46
|
+
links: {
|
47
|
+
:"posts.comments" => "http://localhost:3000/api/comments/{posts.comments}"
|
48
|
+
}
|
49
|
+
}.with_indifferent_access
|
50
|
+
end
|
51
|
+
end
|