json_api_resource 0.4.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86a8a63b0785492a9f4964e23c9a74a06a5bb627
4
- data.tar.gz: 2185bd954e63235cacc9e76d231cb77f5a4c0865
3
+ metadata.gz: 2e08b47087e75a6dcbf21993cca2a1cca415b13e
4
+ data.tar.gz: f8eb103c7b7259c308eb31cca0bfac82b26654e0
5
5
  SHA512:
6
- metadata.gz: f1a7eb2e44dbf6b7ee3d0eb50996f4743b56559596a26794b5ddabbe831d39df684b05670486c3e14e37e5b7ca312382282500fc6dec299233932269770e2184
7
- data.tar.gz: a45673f3cebca622d6a30c26f440f8cd981ad61a7e4a265003a3089d9ae45f9f1012ae9f335d1680a36aaf31b1ce51bcffa9e1074335c243de988b421b9b7e5d
6
+ metadata.gz: 157cab83d13c35c97dceab05d4008a608a029096506812ab20e46506dc52ab13e4b04813f2bc747473589eac70df8ecd6c9472ce5fff9211ecb2820b83f08c2e
7
+ data.tar.gz: 94127db2d17bfb3874cc3c15110e83007d691ae6323026714282f4de9a6598ebe76a22be1f23a78906a502f2ff1b94d014cb3e43fba00f1d5f190924c41b0792
@@ -2,7 +2,7 @@ module JsonApiResource
2
2
  module Cacheable
3
3
  extend ActiveSupport::Concern
4
4
  def cache_key
5
- @cache_key ||= Digest::SHA256.hexdigest(self.attributes.to_s)
5
+ @cache_key ||= Digest::SHA256.hexdigest(self.to_json)
6
6
  end
7
7
  end
8
8
  end
@@ -0,0 +1,16 @@
1
+ module JsonApiResource
2
+ module Clientable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :client_class
7
+ self.client_class = nil
8
+
9
+ class << self
10
+ def wraps(client)
11
+ self.client_class = client
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,23 +1,12 @@
1
1
  module JsonApiResource
2
2
  module Conversions
3
- def Address(*args)
4
- case args.first
5
- when Address then args.first
6
- when Hash then Address.new(args.first)
7
- when Address.client then Address.new(args.first.attributes)
8
- when Array then args.first.map { |attr| Address(attr) }
9
- when Integer then Address.new(* args)
10
- when String then Address.new(* args.first.split(':'). map(&:to_i))
11
- else raise TypeError, "Cannot convert #{ args.inspect} to Address"
12
- end
13
- end
14
-
15
3
  def ApiErrors(*args)
16
4
  case args.first
17
5
  when ActiveModel::Errors then args.first
18
6
  when Hash then args.first
19
7
  when Array then { base: args }
20
8
  when String then { base: [args] }
9
+ when NilClass then { base: [[]]}
21
10
  else raise TypeError, "Cannot convert #{ args.inspect} to Error"
22
11
  end
23
12
  end
@@ -55,19 +44,12 @@ module JsonApiResource
55
44
  end
56
45
  end
57
46
 
58
- def Symbolize(*args)
59
- case args.first
60
- when String then args.first.underscore.parameterize('_').to_sym
61
- else args.first
62
- end
63
- end
64
-
65
47
  def ApiResource(klass, *args)
66
48
  case args.first
67
49
  when klass then args.first
68
50
  when Hash then klass.new(args.first)
69
- when klass.client then klass.new(args.first.attributes)
70
- when Array then args.first.map { |attr| JsonApiResource(attr, klass) }
51
+ when klass.client_class then klass.new(args.first.attributes)
52
+ when Array then args.first.map { |attr| ApiResource(klass, attr) }
71
53
  else raise TypeError, "Cannot convert #{ args.inspect} to #{klass}"
72
54
  end
73
55
  end
@@ -0,0 +1,15 @@
1
+ module JsonApiResource
2
+ module Handlers
3
+ class FindHandler
4
+ attr_accessor :result
5
+
6
+ def initialize(results)
7
+ self.result = results.first
8
+
9
+ self.result.meta = results.meta
10
+
11
+ self.result.linked_data = results.try(:linked_data)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module JsonApiResource
2
+ module Handlers
3
+ autoload :FindHandler, 'json_api_resource/handlers/find_handler'
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ module JsonApiResource
2
+ class JsonApiResourceError < StandardError
3
+ def initialize(opts = {})
4
+ @klass = opts.fetch :class, JsonApiResource::Resource
5
+ @message = opts.fetch :message, ""
6
+ end
7
+
8
+ def message
9
+ "#{@klass}: #{@message}"
10
+ end
11
+ end
12
+ end
@@ -2,91 +2,48 @@ module JsonApiResource
2
2
  module Queryable
3
3
  extend ActiveSupport::Concern
4
4
 
5
- attr_accessor :meta
6
- attr_accessor :linked_data
7
- attr_accessor :errors
5
+ included do
8
6
 
9
- MAX_PAGES_FOR_ALL = 25
7
+ define_model_callbacks :save, :update_attributes
10
8
 
11
- module ClassMethods
9
+ around_save :catch_errors
10
+ around_update_attributes :catch_errors
12
11
 
13
- include JsonApiResource::Conversions
12
+ class << self
14
13
 
15
- def find(id)
16
- return nil unless id.present?
17
- results = self.client_klass.find(id).map! do |result|
18
- self.new(:client => result)
19
- end
20
- results.size == 1 ? single_result(results) : results
21
- rescue JsonApiClient::Errors::ServerError => e
22
- pretty_error e
23
- end
14
+ MAX_PAGES_FOR_ALL = 25
24
15
 
25
- def create(attr = {})
26
- new(:client => self.client_klass.create(attr)).save
27
- end
16
+ def find(id)
17
+ return nil unless id.present?
28
18
 
29
- def where(opts = {})
30
- opts[:per_page] = opts.fetch(:per_page, self.per_page) if self.per_page
31
- (self.client_klass.where(opts).all).map! do |result|
32
- self.new(:client => result)
19
+ results = request(:find, id: id)
20
+ JsonApiResource::Handlers::FindHandler.new(results).result # <= <#JsonApiclient::ResultSet @errors => <...>, @data => <...>, @linked_data => <...>>
33
21
  end
34
- rescue JsonApiClient::Errors::ServerError => e
35
- pretty_error e
36
- end
37
22
 
38
- def all_without_pagination(opts = {})
39
- page_total = 1
40
- current_page = 1
41
- all_results = []
42
- until (current_page > page_total) || (current_page > MAX_PAGES_FOR_ALL)
43
- page_of_results = where({:page => current_page}.merge(opts))
44
- all_results << page_of_results
45
- page_total = page_of_results.meta[:total_pages] || 1
46
- current_page = current_page + 1
23
+ def where(opts = {})
24
+ opts[:per_page] = opts.fetch(:per_page, self.per_page)
25
+ request(:where, opts)
47
26
  end
48
- JsonApiClient::ResultSet.new(all_results.flatten.compact)
49
27
  end
50
28
 
51
- private
52
-
53
- # When we return a collection, these extra attributes on top of the result array from JsonApiClient are present.
54
- # When we find just one thing and return the first element like ActiveRecord would,
55
- # we lose these things. We want them, so we will assign them to this object.
56
- def single_result(results)
57
- result = results.first
58
-
59
- result.meta = results.meta
60
-
61
- result.linked_data = results.linked_data if results.respond_to? :linked_data
62
-
63
- return result
29
+
30
+ def save
31
+ request :save
64
32
  end
65
33
 
66
- def pretty_error(e)
67
- case e.class.to_s
68
-
69
- when 'JsonApiClient::Errors::NotFound'
70
- error_response 404, { name: "RecordNotFound", message: e.message }
71
-
72
- when 'JsonApiClient::Errors::UnexpectedStatus'
73
- error_response e.code, { name: "UnexpectedStatus", message: e.message }
74
-
75
- else
76
- error_response 500, { name: "ServerError", message: e.message }
77
- end
34
+ def update_attributes(attrs = {})
35
+ request :update_attributes, attrs
78
36
  end
79
37
 
80
- def error_response(status, error)
81
- result = JsonApiClient::ResultSet.new
38
+ def catch_errors
39
+ yield
82
40
 
83
- result.meta = {status: status}
84
-
85
- result.errors = ActiveModel::Errors.new(result)
86
- result.errors.add(error[:name], Array(error[:message]).join(', '))
87
-
88
- result
41
+ self.errors ||= ActiveModel::Errors.new(self)
42
+ ApiErrors(self.client.errors).each do | k,messages|
43
+ self.errors.add(k.to_sym, Array(messages).join(', '))
44
+ end
45
+ self.errors
89
46
  end
90
47
  end
91
48
  end
92
- end
49
+ end
@@ -0,0 +1,72 @@
1
+ module JsonApiResource
2
+ module Requestable
3
+ extend ActiveModel::Callbacks
4
+ extend ActiveSupport::Concern
5
+ extend ActiveSupport::Callbacks
6
+
7
+ included do
8
+ def request(action, *args)
9
+ run_callbacks action do
10
+ self.client.send(action, *args)
11
+ end
12
+ self
13
+
14
+ rescue JsonApiClient::Errors::ServerError => e
15
+ add_error e
16
+ end
17
+
18
+ class << self
19
+ def request(action, *args)
20
+ result = self.client_class.send(action, *args)
21
+
22
+ result = result.all if result.is_a? JsonApiClient::Scope
23
+
24
+ JsonApiClient::ResultSet.new(
25
+ result.map do |result|
26
+ new(client: result)
27
+ end
28
+ )
29
+ rescue JsonApiClient::Errors::ServerError => e
30
+ empty_set_with_errors e
31
+ end
32
+
33
+ def empty_set_with_errors(e)
34
+ append_errors e do |status, error|
35
+ error_response status, error
36
+ end
37
+ end
38
+
39
+ def append_errors(e, &block)
40
+ case e.class.to_s
41
+
42
+ when "JsonApiClient::Errors::NotFound"
43
+ yield 404, { name: "RecordNotFound", message: e.message }
44
+
45
+ when "JsonApiClient::Errors::UnexpectedStatus"
46
+ yield e.code, { name: "UnexpectedStatus", message: e.message }
47
+
48
+ else
49
+ yield 500, { name: "ServerError", message: e.message }
50
+ end
51
+ end
52
+
53
+ def error_response(status, error)
54
+ result = JsonApiClient::ResultSet.new
55
+
56
+ result.meta = {status: status}
57
+
58
+ result.errors = ActiveModel::Errors.new(result)
59
+ result.errors.add(error[:name], Array(error[:message]).join(', '))
60
+
61
+ result
62
+ end
63
+ end
64
+
65
+ def add_error(e, &block)
66
+ self.class.append_errors e do |status, error|
67
+ errors.add error[:name], error[:message]
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,24 +1,37 @@
1
+ require 'active_support'
2
+ require 'active_support/callbacks'
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/module'
5
+ require 'active_support/core_ext/class/attribute'
6
+ require 'active_model'
7
+ require 'active_model/model'
8
+ require 'active_model/validations'
9
+ require 'active_model/callbacks'
10
+
1
11
  module JsonApiResource
2
12
  class Resource
3
13
  include ActiveModel::Model
4
14
  include ActiveModel::Validations
5
15
  extend ActiveModel::Callbacks
6
16
 
17
+ extend ActiveSupport::Callbacks
18
+
19
+ include JsonApiResource::Clientable
7
20
  include JsonApiResource::Schemable
8
21
  include JsonApiResource::Queryable
22
+ include JsonApiResource::Requestable
9
23
  include JsonApiResource::Conversions
10
24
  include JsonApiResource::Cacheable
11
25
 
12
26
  attr_accessor :client, :cache_expires_in
13
- class_attribute :client_klass, :per_page
14
-
15
- define_model_callbacks :save, :update_attributes
27
+ class_attribute :per_page
16
28
 
17
- around_save :update_meta
18
- around_update_attributes :update_meta
29
+ delegate :to_json, to: :attributes
19
30
 
20
31
  def initialize(opts={})
21
- self.client = self.client_klass.new(self.schema)
32
+ raise( JsonApiResourceError, class: self.class, message: "A resource must have a client class" ) unless client_class.present?
33
+
34
+ self.client = self.client_class.new(self.schema)
22
35
  self.errors = ActiveModel::Errors.new(self)
23
36
  self.attributes = opts
24
37
  end
@@ -31,25 +44,9 @@ module JsonApiResource
31
44
  !new_record?
32
45
  end
33
46
 
34
- def save
35
- run_callbacks :save do
36
- self.client.save
37
- end
38
- rescue JsonApiClient::Errors::ServerError => e
39
- pretty_error e
40
- end
41
-
42
- def update_attributes(attrs = {})
43
- run_callbacks :update_attributes do
44
- self.client.update_attributes(attrs)
45
- end
46
- rescue JsonApiClient::Errors::ServerError => e
47
- pretty_error e
48
- end
49
-
50
47
  def attributes=(attr = {})
51
48
  client_params = attr.delete(:client)
52
- if attr.is_a? self.client_klass
49
+ if attr.is_a? self.client_class
53
50
  self.client = attr
54
51
  elsif client_params
55
52
  self.client = client_params
@@ -62,55 +59,41 @@ module JsonApiResource
62
59
 
63
60
  def method_missing(method, *args, &block)
64
61
  if match = method.to_s.match(/^(.*=)$/)
65
- self.client.send(match[1], args.first)
62
+ self.client.send(match[0], args.first)
66
63
  elsif self.client.respond_to?(method.to_sym)
67
- is_method = self.client.methods.include?(method.to_sym)
68
- argument_count = (is_method ? self.client.method(method.to_sym).arity : 0)
69
- argument_count = args.length if argument_count == -1
70
- if (argument_count == 0) || args.blank?
71
- self.client.send(method)
72
- else
73
- self.client.send(method, *args.take(argument_count))
74
- end
64
+ self.client.send(method, *args)
75
65
  else
76
66
  super
77
67
  end
78
68
 
79
69
  rescue JsonApiClient::Errors::ServerError => e
80
- pretty_error e
81
- end
82
-
83
- def update_meta
84
- yield
85
-
86
- self.errors ||= ActiveModel::Errors.new(self)
87
- ApiErrors(self.client.errors).each do | k,messages|
88
- self.errors.add(k.to_sym, Array(messages).join(', '))
89
- end
90
- self.errors
91
-
92
- self.meta = self.client.last_request_meta
70
+ add_error e
71
+ rescue ArgumentError => e
72
+ raise JsonApiResourceError, class: self.class, message: "#{method}: #{e.message}"
93
73
  end
94
74
 
95
75
  def self.method_missing(method, *args, &block)
96
76
  if match = method.to_s.match(/^(.*)=$/)
97
- self.client_klass.send(match[1], args.first)
98
-
99
- elsif self.client_klass.respond_to?(method.to_sym)
100
- results = self.client_klass.send(method, *args)
77
+ self.client_class.send(match[0], args.first)
78
+
79
+ elsif self.client_class.respond_to?(method.to_sym)
80
+ results = self.client_class.send(method, *args)
101
81
 
102
82
  if results.is_a? JsonApiClient::ResultSet
103
- results.map! do |result|
83
+ results = results.map do |result|
104
84
  self.new(:client => result)
105
85
  end
106
86
  end
87
+
107
88
  results
89
+
108
90
  else
109
91
  super
110
92
  end
111
-
112
93
  rescue JsonApiClient::Errors::ServerError => e
113
- pretty_error e
94
+ empty_set_with_errors e
95
+ rescue ArgumentError => e
96
+ raise JsonApiResourceError, class: self, message: "#{method}: #{e.message}"
114
97
  end
115
98
 
116
99
  def respond_to_missing?(method_name, include_private = false)
@@ -118,7 +101,7 @@ module JsonApiResource
118
101
  end
119
102
 
120
103
  def self.respond_to_missing?(method_name, include_private = false)
121
- client_klass.respond_to?(method_name.to_sym) || super
104
+ client_class.respond_to?(method_name.to_sym) || super
122
105
  end
123
106
  end
124
107
  end
@@ -6,21 +6,19 @@ module JsonApiResource
6
6
  included do
7
7
  class_attribute :schema
8
8
  self.schema = {}
9
- end
9
+
10
+ class << self
11
+ def properties(opts = {})
12
+ self.schema = schema.dup
13
+ opts.each_pair do |name, default|
14
+ property name, default
15
+ end
16
+ end
10
17
 
11
- module ClassMethods
12
- def property(opts = {})
13
- opts.each do |attr_name,default|
14
- self.schema[attr_name.to_sym] = default || nil
18
+ def property(name, default = nil)
19
+ self.schema = schema.merge name.to_sym => default
15
20
  end
16
21
  end
17
22
  end
18
-
19
- protected
20
-
21
- def load_schema
22
- self.client = self.client_klass.new(self.schema)
23
- end
24
-
25
23
  end
26
24
  end
@@ -1,3 +1,3 @@
1
1
  module JsonApiResource
2
- VERSION = "0.4.9"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,9 +1,13 @@
1
1
  require 'json_api_client'
2
2
 
3
3
  module JsonApiResource
4
- autoload :Conversions, 'json_api_resource/conversions'
5
- autoload :Queryable, 'json_api_resource/queryable'
6
- autoload :Schemable, 'json_api_resource/schemable'
7
- autoload :Resource, 'json_api_resource/resource'
8
- autoload :Cacheable, 'json_api_resource/cacheable'
4
+ autoload :Cacheable, 'json_api_resource/cacheable'
5
+ autoload :Clientable, 'json_api_resource/clientable'
6
+ autoload :Conversions, 'json_api_resource/conversions'
7
+ autoload :Handlers, 'json_api_resource/handlers'
8
+ autoload :JsonApiResourceError, 'json_api_resource/json_api_resource_error'
9
+ autoload :Queryable, 'json_api_resource/queryable'
10
+ autoload :Resource, 'json_api_resource/resource'
11
+ autoload :Requestable, 'json_api_resource/requestable'
12
+ autoload :Schemable, 'json_api_resource/schemable'
9
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_api_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Sislow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-13 00:00:00.000000000 Z
11
+ date: 2016-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json_api_client
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: webmock
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -63,12 +91,17 @@ files:
63
91
  - Rakefile
64
92
  - lib/json_api_resource.rb
65
93
  - lib/json_api_resource/cacheable.rb
94
+ - lib/json_api_resource/clientable.rb
66
95
  - lib/json_api_resource/conversions.rb
96
+ - lib/json_api_resource/handlers.rb
97
+ - lib/json_api_resource/handlers/find_handler.rb
98
+ - lib/json_api_resource/json_api_resource_error.rb
67
99
  - lib/json_api_resource/queryable.rb
100
+ - lib/json_api_resource/requestable.rb
68
101
  - lib/json_api_resource/resource.rb
69
102
  - lib/json_api_resource/schemable.rb
70
103
  - lib/json_api_resource/version.rb
71
- homepage: http://gitlab.corp.avvo.com/api/api_resource
104
+ homepage: https://github.com/avvo/json_api_resource
72
105
  licenses:
73
106
  - MIT
74
107
  metadata: {}
@@ -88,8 +121,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
121
  version: '0'
89
122
  requirements: []
90
123
  rubyforge_project:
91
- rubygems_version: 2.4.6
124
+ rubygems_version: 2.4.5
92
125
  signing_key:
93
126
  specification_version: 4
94
127
  summary: Build wrapper/adapter objects around JsonApiClient instances
95
128
  test_files: []
129
+ has_rdoc: