json_api_client 0.9.6 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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