json_api_client 0.9.6 → 1.0.0.beta

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +274 -84
  3. data/lib/json_api_client.rb +9 -4
  4. data/lib/json_api_client/connection.rb +5 -5
  5. data/lib/json_api_client/error_collector.rb +29 -0
  6. data/lib/json_api_client/errors.rb +7 -5
  7. data/lib/json_api_client/helpers.rb +3 -0
  8. data/lib/json_api_client/helpers/associable.rb +4 -3
  9. data/lib/json_api_client/helpers/attributable.rb +15 -46
  10. data/lib/json_api_client/helpers/custom_endpoints.rb +3 -10
  11. data/lib/json_api_client/helpers/dynamic_attributes.rb +61 -0
  12. data/lib/json_api_client/helpers/linkable.rb +28 -18
  13. data/lib/json_api_client/helpers/paginatable.rb +13 -0
  14. data/lib/json_api_client/helpers/parsable.rb +1 -1
  15. data/lib/json_api_client/helpers/queryable.rb +4 -3
  16. data/lib/json_api_client/helpers/requestable.rb +60 -0
  17. data/lib/json_api_client/linking.rb +7 -0
  18. data/lib/json_api_client/linking/included_data.rb +40 -0
  19. data/lib/json_api_client/linking/links.rb +29 -0
  20. data/lib/json_api_client/linking/top_level_links.rb +30 -0
  21. data/lib/json_api_client/meta_data.rb +10 -0
  22. data/lib/json_api_client/middleware/json_request.rb +3 -4
  23. data/lib/json_api_client/middleware/status.rb +10 -1
  24. data/lib/json_api_client/paginating.rb +5 -0
  25. data/lib/json_api_client/paginating/paginator.rb +80 -0
  26. data/lib/json_api_client/parsers.rb +5 -0
  27. data/lib/json_api_client/parsers/parser.rb +55 -0
  28. data/lib/json_api_client/query.rb +2 -7
  29. data/lib/json_api_client/query/builder.rb +126 -0
  30. data/lib/json_api_client/query/requestor.rb +77 -0
  31. data/lib/json_api_client/resource.rb +3 -59
  32. data/lib/json_api_client/result_set.rb +11 -29
  33. data/lib/json_api_client/schema.rb +15 -30
  34. data/lib/json_api_client/version.rb +1 -1
  35. metadata +36 -19
  36. data/lib/json_api_client/link.rb +0 -11
  37. data/lib/json_api_client/link_definition.rb +0 -27
  38. data/lib/json_api_client/linked_data.rb +0 -75
  39. data/lib/json_api_client/parser.rb +0 -63
  40. data/lib/json_api_client/query/base.rb +0 -38
  41. data/lib/json_api_client/query/create.rb +0 -17
  42. data/lib/json_api_client/query/custom.rb +0 -22
  43. data/lib/json_api_client/query/destroy.rb +0 -12
  44. data/lib/json_api_client/query/find.rb +0 -19
  45. data/lib/json_api_client/query/linked.rb +0 -24
  46. data/lib/json_api_client/query/update.rb +0 -13
  47. data/lib/json_api_client/scope.rb +0 -48
@@ -6,7 +6,7 @@ module JsonApiClient
6
6
  def initialize(options = {})
7
7
  site = options.fetch(:site)
8
8
  @faraday = Faraday.new(site) do |builder|
9
- builder.request :url_encoded
9
+ builder.request :json
10
10
  builder.use Middleware::JsonRequest
11
11
  builder.use Middleware::Status
12
12
  builder.use Middleware::ParseJson
@@ -15,7 +15,7 @@ module JsonApiClient
15
15
  yield(self) if block_given?
16
16
  end
17
17
 
18
- # insert middleware before ParseJson - middleware executed in reverse order -
18
+ # insert middleware before ParseJson - middleware executed in reverse order -
19
19
  # inserted middleware will run after json parsed
20
20
  def use(middleware, *args, &block)
21
21
  return if faraday.builder.locked?
@@ -26,9 +26,9 @@ module JsonApiClient
26
26
  faraday.builder.delete(middleware)
27
27
  end
28
28
 
29
- def execute(query)
30
- faraday.send(query.request_method, query.path, query.params, query.headers)
29
+ def run(request_method, path, params = {}, headers = {})
30
+ faraday.send(request_method, path, params, headers)
31
31
  end
32
32
 
33
33
  end
34
- end
34
+ end
@@ -0,0 +1,29 @@
1
+ module JsonApiClient
2
+ class ErrorCollector
3
+ class Error
4
+ include Helpers::DynamicAttributes
5
+
6
+ def initialize(attrs = {})
7
+ attrs = {
8
+ title: attrs
9
+ } if attrs.is_a?(String)
10
+ self.attributes = attrs
11
+ end
12
+ end
13
+
14
+ attr_reader :errors
15
+ extend Forwardable
16
+ def_delegators :errors, :length, :present?
17
+
18
+ def initialize(error_data)
19
+ @errors = Array(error_data).map do |datum|
20
+ Error.new(datum)
21
+ end
22
+ end
23
+
24
+ def full_messages
25
+ errors.map(&:title)
26
+ end
27
+
28
+ end
29
+ end
@@ -10,13 +10,15 @@ module JsonApiClient
10
10
  class ClientError < ApiError
11
11
  end
12
12
 
13
+ class AccessDenied < ClientError
14
+ end
15
+
16
+ class ConnectionError < ApiError
17
+ end
18
+
13
19
  class ServerError < ApiError
14
- attr_reader :uri
15
- def initialize(uri)
16
- @uri = uri
17
- end
18
20
  def message
19
- "Internal server error at: #{uri.to_s}"
21
+ "Internal server error"
20
22
  end
21
23
  end
22
24
 
@@ -3,10 +3,13 @@ module JsonApiClient
3
3
  autoload :Associable, 'json_api_client/helpers/associable'
4
4
  autoload :Attributable, 'json_api_client/helpers/attributable'
5
5
  autoload :CustomEndpoints, 'json_api_client/helpers/custom_endpoints'
6
+ autoload :DynamicAttributes, 'json_api_client/helpers/dynamic_attributes'
6
7
  autoload :Initializable, 'json_api_client/helpers/initializable'
7
8
  autoload :Linkable, 'json_api_client/helpers/linkable'
9
+ autoload :Paginatable, 'json_api_client/helpers/paginatable'
8
10
  autoload :Parsable, 'json_api_client/helpers/parsable'
9
11
  autoload :Queryable, 'json_api_client/helpers/queryable'
12
+ autoload :Requestable, 'json_api_client/helpers/requestable'
10
13
  autoload :Schemable, 'json_api_client/helpers/schemable'
11
14
  autoload :Serializable, 'json_api_client/helpers/serializable'
12
15
  end
@@ -30,11 +30,12 @@ module JsonApiClient
30
30
  def path(params = nil)
31
31
  parts = [table_name]
32
32
  if params
33
- slurp = params.symbolize_keys.slice(*prefix_params)
33
+ filters = params.fetch(:filter, params)
34
+ slurp = filters.slice(*prefix_params)
34
35
  prefix_params.each do |param|
35
- params.delete(param)
36
+ filters.delete(param)
36
37
  end
37
- parts.unshift(prefix_path % slurp)
38
+ parts.unshift(prefix_path % slurp.symbolize_keys)
38
39
  else
39
40
  parts.unshift(prefix_path)
40
41
  end
@@ -4,19 +4,22 @@ module JsonApiClient
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- attr_reader :attributes
7
+ include DynamicAttributes
8
8
  attr_accessor :errors
9
9
  initializer do |obj, params|
10
- obj.attributes = params
10
+ obj.attributes = params.merge(obj.class.default_attributes)
11
11
  end
12
12
  end
13
13
 
14
- def attributes=(attrs = {})
15
- @attributes ||= {}.with_indifferent_access
14
+ module ClassMethods
15
+ def load(params)
16
+ new(params).tap do |resource|
17
+ resource.mark_as_persisted!
18
+ end
19
+ end
16
20
 
17
- return @attributes unless attrs.present?
18
- attrs.each do |key, value|
19
- set_attribute(key, value)
21
+ def default_attributes
22
+ {type: table_name}
20
23
  end
21
24
  end
22
25
 
@@ -25,8 +28,12 @@ module JsonApiClient
25
28
  save
26
29
  end
27
30
 
31
+ def mark_as_persisted!
32
+ @persisted = true
33
+ end
34
+
28
35
  def persisted?
29
- attributes[primary_key].present?
36
+ !!@persisted && has_attribute?(primary_key)
30
37
  end
31
38
 
32
39
  def query_params
@@ -37,46 +44,8 @@ module JsonApiClient
37
44
  attributes.fetch(primary_key, "").to_s
38
45
  end
39
46
 
40
- def [](key)
41
- read_attribute(key)
42
- end
43
-
44
- def []=(key, value)
45
- set_attribute(key, value)
46
- end
47
-
48
- def respond_to?(method, include_private = false)
49
- if (method.to_s =~ /^(.*)=$/) || has_attribute?(method)
50
- true
51
- else
52
- super
53
- end
54
- end
55
-
56
47
  protected
57
48
 
58
- def method_missing(method, *args, &block)
59
- if method.to_s =~ /^(.*)=$/
60
- set_attribute($1, args.first)
61
- elsif has_attribute?(method)
62
- attributes[method]
63
- else
64
- super
65
- end
66
- end
67
-
68
- def read_attribute(name)
69
- attributes.fetch(name, nil)
70
- end
71
-
72
- def set_attribute(name, value)
73
- attributes[name] = value
74
- end
75
-
76
- def has_attribute?(attr_name)
77
- attributes.has_key?(attr_name)
78
- end
79
-
80
49
  def ==(other)
81
50
  self.class == other.class && attributes == other.attributes
82
51
  end
@@ -18,11 +18,8 @@ module JsonApiClient
18
18
  end
19
19
  metaclass.instance_eval do
20
20
  define_method(name) do |*params|
21
- input = {
22
- name: name,
23
- params: request_params = params.first || {}
24
- }.merge(options)
25
- run_request(Query::Custom.new(self, input))
21
+ request_params = params.first || {}
22
+ requestor.custom(name, options, request_params)
26
23
  end
27
24
  end
28
25
  end
@@ -31,11 +28,7 @@ module JsonApiClient
31
28
  define_method name do |*params|
32
29
  request_params = params.first || {}
33
30
  request_params[self.class.primary_key] = attributes.fetch(primary_key)
34
- input = {
35
- name: name,
36
- params: request_params
37
- }.merge(options)
38
- run_request(Query::Custom.new(self.class, input))
31
+ self.class.requestor.custom(name, options, request_params)
39
32
  end
40
33
  end
41
34
  end
@@ -0,0 +1,61 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module DynamicAttributes
4
+ extend ActiveSupport::Concern
5
+
6
+ def attributes
7
+ @attributes
8
+ end
9
+
10
+ def attributes=(attrs = {})
11
+ @attributes ||= {}.with_indifferent_access
12
+
13
+ return @attributes unless attrs.present?
14
+ attrs.each do |key, value|
15
+ set_attribute(key, value)
16
+ end
17
+ end
18
+
19
+ def [](key)
20
+ read_attribute(key)
21
+ end
22
+
23
+ def []=(key, value)
24
+ set_attribute(key, value)
25
+ end
26
+
27
+ def respond_to_missing?(method, include_private = false)
28
+ if (method.to_s =~ /^(.*)=$/) || has_attribute?(method)
29
+ true
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ def has_attribute?(attr_name)
36
+ attributes.has_key?(attr_name)
37
+ end
38
+
39
+ protected
40
+
41
+ def method_missing(method, *args, &block)
42
+ if method.to_s =~ /^(.*)=$/
43
+ set_attribute($1, args.first)
44
+ elsif has_attribute?(method)
45
+ attributes[method]
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def read_attribute(name)
52
+ attributes.fetch(name, nil)
53
+ end
54
+
55
+ def set_attribute(name, value)
56
+ attributes[name] = value
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -4,35 +4,45 @@ module JsonApiClient
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- attr_accessor :links,
8
- :linked_data
7
+ class_attribute :linker
8
+ self.linker = Linking::Links
9
+
10
+ # the links for this resource
11
+ attr_accessor :links
12
+
13
+ # reference to all of the preloaded data
14
+ attr_accessor :linked_data
9
15
 
10
16
  initializer do |obj, params|
11
- if params && links = params.delete("links")
12
- obj.links = links
13
- end
17
+ links = params && params.delete("links")
18
+ links ||= {}
19
+ obj.links = obj.linker.new(links)
14
20
  end
15
21
  end
16
22
 
17
- def method_missing(method, *args)
18
- return super unless has_link?(method)
19
-
20
- linked_data.data_for(method, links[method.to_s])
23
+ def as_link
24
+ {
25
+ :type => self.class.table_name,
26
+ primary_key => self[primary_key]
27
+ }
21
28
  end
22
29
 
23
- def respond_to?(symbol, include_all = false)
24
- return true if has_link?(symbol)
25
- super
30
+ def attributes
31
+ super.tap do |attrs|
32
+ attrs.merge!(links: links.attributes) if links.present?
33
+ end
26
34
  end
27
35
 
28
- private
36
+ def method_missing(method, *args)
37
+ return super unless links && links.has_attribute?(method)
38
+ linked_data.data_for(method, links[method])
39
+ end
29
40
 
30
- def has_link?(symbol)
31
- links &&
32
- links.has_key?(symbol.to_s) &&
33
- linked_data &&
34
- linked_data.has_link?(symbol.to_s)
41
+ def respond_to_missing?(symbol, include_all = false)
42
+ return true if links && links.has_attribute?(symbol)
43
+ super
35
44
  end
45
+
36
46
  end
37
47
  end
38
48
  end
@@ -0,0 +1,13 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module Paginatable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :paginator
8
+ self.paginator = Paginating::Paginator
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -5,7 +5,7 @@ module JsonApiClient
5
5
 
6
6
  included do
7
7
  class_attribute :parser
8
- self.parser = Parser
8
+ self.parser = Parsers::Parser
9
9
  end
10
10
 
11
11
  module ClassMethods
@@ -6,16 +6,17 @@ module JsonApiClient
6
6
  included do
7
7
  class << self
8
8
  extend Forwardable
9
- def_delegators :new_scope, :where, :order, :includes, :all, :paginate, :page, :first
9
+ def_delegators :new_scope, :where, :order, :includes, :select, :all, :paginate, :page, :first
10
10
  end
11
- class_attribute :connection_class, :connection_object, :connection_options
11
+ class_attribute :connection_class, :connection_object, :connection_options, :query_builder
12
12
  self.connection_class = Connection
13
13
  self.connection_options = {}
14
+ self.query_builder = Query::Builder
14
15
  end
15
16
 
16
17
  module ClassMethods
17
18
  def new_scope
18
- Scope.new(self)
19
+ query_builder.new(self)
19
20
  end
20
21
 
21
22
  def connection(&block)
@@ -0,0 +1,60 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module Requestable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attr_accessor :last_result_set, :errors
8
+ class_attribute :requestor_class
9
+ self.requestor_class = Query::Requestor
10
+ end
11
+
12
+ module ClassMethods
13
+ def find(conditions)
14
+ requestor.find(conditions)
15
+ end
16
+
17
+ def create(conditions = {})
18
+ new(conditions).tap do |resource|
19
+ resource.save
20
+ end
21
+ end
22
+
23
+ def requestor
24
+ @requestor ||= requestor_class.new(self)
25
+ end
26
+ end
27
+
28
+ def save
29
+ self.last_result_set = if persisted?
30
+ self.class.requestor.update(self)
31
+ else
32
+ self.class.requestor.create(self)
33
+ end
34
+
35
+ if last_result_set.has_errors?
36
+ self.errors = last_result_set.errors
37
+ false
38
+ else
39
+ self.errors.clear if self.errors
40
+ mark_as_persisted!
41
+ if updated = last_result_set.first
42
+ self.attributes = updated.attributes
43
+ end
44
+ true
45
+ end
46
+ end
47
+
48
+ def destroy
49
+ self.last_result_set = self.class.requestor.destroy(self)
50
+ if !last_result_set.has_errors?
51
+ self.attributes.clear
52
+ true
53
+ else
54
+ false
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end