carwow-json_api_client 1.19.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +706 -0
  4. data/Rakefile +32 -0
  5. data/lib/json_api_client.rb +30 -0
  6. data/lib/json_api_client/associations.rb +8 -0
  7. data/lib/json_api_client/associations/base_association.rb +33 -0
  8. data/lib/json_api_client/associations/belongs_to.rb +31 -0
  9. data/lib/json_api_client/associations/has_many.rb +8 -0
  10. data/lib/json_api_client/associations/has_one.rb +16 -0
  11. data/lib/json_api_client/connection.rb +41 -0
  12. data/lib/json_api_client/error_collector.rb +91 -0
  13. data/lib/json_api_client/errors.rb +107 -0
  14. data/lib/json_api_client/formatter.rb +145 -0
  15. data/lib/json_api_client/helpers.rb +9 -0
  16. data/lib/json_api_client/helpers/associatable.rb +88 -0
  17. data/lib/json_api_client/helpers/callbacks.rb +27 -0
  18. data/lib/json_api_client/helpers/dirty.rb +75 -0
  19. data/lib/json_api_client/helpers/dynamic_attributes.rb +78 -0
  20. data/lib/json_api_client/helpers/uri.rb +9 -0
  21. data/lib/json_api_client/implementation.rb +12 -0
  22. data/lib/json_api_client/included_data.rb +58 -0
  23. data/lib/json_api_client/linking.rb +6 -0
  24. data/lib/json_api_client/linking/links.rb +22 -0
  25. data/lib/json_api_client/linking/top_level_links.rb +39 -0
  26. data/lib/json_api_client/meta_data.rb +19 -0
  27. data/lib/json_api_client/middleware.rb +7 -0
  28. data/lib/json_api_client/middleware/json_request.rb +26 -0
  29. data/lib/json_api_client/middleware/parse_json.rb +31 -0
  30. data/lib/json_api_client/middleware/status.rb +67 -0
  31. data/lib/json_api_client/paginating.rb +6 -0
  32. data/lib/json_api_client/paginating/nested_param_paginator.rb +140 -0
  33. data/lib/json_api_client/paginating/paginator.rb +89 -0
  34. data/lib/json_api_client/parsers.rb +5 -0
  35. data/lib/json_api_client/parsers/parser.rb +102 -0
  36. data/lib/json_api_client/query.rb +6 -0
  37. data/lib/json_api_client/query/builder.rb +239 -0
  38. data/lib/json_api_client/query/requestor.rb +73 -0
  39. data/lib/json_api_client/relationships.rb +6 -0
  40. data/lib/json_api_client/relationships/relations.rb +55 -0
  41. data/lib/json_api_client/relationships/top_level_relations.rb +30 -0
  42. data/lib/json_api_client/request_params.rb +57 -0
  43. data/lib/json_api_client/resource.rb +643 -0
  44. data/lib/json_api_client/result_set.rb +25 -0
  45. data/lib/json_api_client/schema.rb +154 -0
  46. data/lib/json_api_client/utils.rb +48 -0
  47. data/lib/json_api_client/version.rb +3 -0
  48. metadata +213 -0
@@ -0,0 +1,9 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ autoload :Callbacks, 'json_api_client/helpers/callbacks'
4
+ autoload :Dirty, 'json_api_client/helpers/dirty'
5
+ autoload :DynamicAttributes, 'json_api_client/helpers/dynamic_attributes'
6
+ autoload :URI, 'json_api_client/helpers/uri'
7
+ autoload :Associatable, 'json_api_client/helpers/associatable'
8
+ end
9
+ end
@@ -0,0 +1,88 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module Associatable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :associations, instance_accessor: false
8
+ self.associations = []
9
+ attr_accessor :__cached_associations
10
+ attr_accessor :__belongs_to_params
11
+ end
12
+
13
+ module ClassMethods
14
+ def _define_association(attr_name, association_klass, options = {})
15
+ attr_name = attr_name.to_sym
16
+ association = association_klass.new(attr_name, self, options)
17
+ self.associations += [association]
18
+ end
19
+
20
+ def _define_relationship_methods(attr_name)
21
+ attr_name = attr_name.to_sym
22
+
23
+ define_method(attr_name) do
24
+ _cached_relationship(attr_name) do
25
+ relationship_definition = relationship_definition_for(attr_name)
26
+ return unless relationship_definition
27
+ relationship_data_for(attr_name, relationship_definition)
28
+ end
29
+ end
30
+
31
+ define_method("#{attr_name}=") do |value|
32
+ _clear_cached_relationship(attr_name)
33
+ relationships.public_send("#{attr_name}=", value)
34
+ end
35
+ end
36
+
37
+ def belongs_to(attr_name, options = {})
38
+ _define_association(attr_name, JsonApiClient::Associations::BelongsTo::Association, options)
39
+
40
+ param = associations.last.param
41
+ define_method(param) do
42
+ _belongs_to_params[param]
43
+ end
44
+
45
+ define_method(:"#{param}=") do |value|
46
+ _belongs_to_params[param] = value
47
+ end
48
+ end
49
+
50
+ def has_many(attr_name, options = {})
51
+ _define_association(attr_name, JsonApiClient::Associations::HasMany::Association, options)
52
+ _define_relationship_methods(attr_name)
53
+ end
54
+
55
+ def has_one(attr_name, options = {})
56
+ _define_association(attr_name, JsonApiClient::Associations::HasOne::Association, options)
57
+ _define_relationship_methods(attr_name)
58
+ end
59
+ end
60
+
61
+ def _belongs_to_params
62
+ self.__belongs_to_params ||= {}
63
+ end
64
+
65
+ def _clear_belongs_to_params
66
+ self.__belongs_to_params = {}
67
+ end
68
+
69
+ def _cached_associations
70
+ self.__cached_associations ||= {}
71
+ end
72
+
73
+ def _clear_cached_relationships
74
+ self.__cached_associations = {}
75
+ end
76
+
77
+ def _clear_cached_relationship(attr_name)
78
+ _cached_associations.delete(attr_name)
79
+ end
80
+
81
+ def _cached_relationship(attr_name)
82
+ return _cached_associations[attr_name] if _cached_associations.has_key?(attr_name)
83
+ _cached_associations[attr_name] = yield
84
+ end
85
+
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,27 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module Callbacks
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ extend ActiveModel::Callbacks
8
+ define_model_callbacks :save, :destroy, :create, :update
9
+ end
10
+
11
+ def save
12
+ run_callbacks :save do
13
+ run_callbacks (persisted? ? :update : :create) do
14
+ super
15
+ end
16
+ end
17
+ end
18
+
19
+ def destroy
20
+ run_callbacks :destroy do
21
+ super
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,75 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module Dirty
4
+
5
+ def changed?
6
+ changed_attributes.present?
7
+ end
8
+
9
+ def changed
10
+ changed_attributes.keys
11
+ end
12
+
13
+ def changed_attributes
14
+ @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
15
+ end
16
+
17
+ def clear_changes_information
18
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
19
+ end
20
+
21
+ def forget_change!(attr)
22
+ @changed_attributes.delete(attr.to_s)
23
+ end
24
+
25
+ def set_all_attributes_dirty
26
+ attributes.each do |k, v|
27
+ set_attribute_was(k, v)
28
+ end
29
+ end
30
+
31
+ def attribute_will_change!(attr)
32
+ return if attribute_changed?(attr)
33
+ set_attribute_was(attr, attributes[attr])
34
+ end
35
+
36
+ def set_attribute_was(attr, value)
37
+ begin
38
+ value = value.duplicable? ? value.clone : value
39
+ changed_attributes[attr] = value
40
+ rescue TypeError, NoMethodError
41
+ end
42
+ end
43
+
44
+ def attribute_was(attr) # :nodoc:
45
+ attribute_changed?(attr) ? changed_attributes[attr] : attributes[attr]
46
+ end
47
+
48
+ def attribute_changed?(attr)
49
+ changed.include?(attr.to_s)
50
+ end
51
+
52
+ def attribute_change(attr)
53
+ [changed_attributes[attr], attributes[attr]] if attribute_changed?(attr)
54
+ end
55
+
56
+ protected
57
+
58
+ def method_missing(method, *args, &block)
59
+ if method.to_s =~ /^(.*)_changed\?$/
60
+ has_attribute?($1) ? attribute_changed?($1) : nil
61
+ elsif method.to_s =~ /^(.*)_was$/
62
+ has_attribute?($1) ? attribute_was($1) : nil
63
+ else
64
+ super
65
+ end
66
+ end
67
+
68
+ def set_attribute(name, value)
69
+ attribute_will_change!(name) if value != attributes[name] || !attributes.has_key?(name)
70
+ super
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,78 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module DynamicAttributes
4
+
5
+ def attributes
6
+ @attributes
7
+ end
8
+
9
+ def attributes=(attrs = {})
10
+ @attributes ||= ActiveSupport::HashWithIndifferentAccess.new
11
+
12
+ return @attributes unless attrs.present?
13
+ attrs.each do |key, value|
14
+ send("#{key}=", value)
15
+ end
16
+ end
17
+
18
+ def [](key)
19
+ read_attribute(key)
20
+ end
21
+
22
+ def []=(key, value)
23
+ set_attribute(key, value)
24
+ end
25
+
26
+ def respond_to_missing?(method, include_private = false)
27
+ if has_attribute?(method) || method.to_s.end_with?('=')
28
+ true
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def has_attribute?(attr_name)
35
+ attributes.has_key?(attr_name)
36
+ end
37
+
38
+ protected
39
+
40
+ def method_missing(method, *args, &block)
41
+ if has_attribute?(method)
42
+ return attributes[method]
43
+ end
44
+
45
+ normalized_method = safe_key_formatter.unformat(method.to_s)
46
+
47
+ if normalized_method.end_with?('=')
48
+ set_attribute(normalized_method[0..-2], args.first)
49
+ else
50
+ super
51
+ end
52
+ end
53
+
54
+ def read_attribute(name)
55
+ attributes.fetch(name, nil)
56
+ end
57
+
58
+ def set_attribute(name, value)
59
+ attributes[name] = value
60
+ end
61
+
62
+ def safe_key_formatter
63
+ @safe_key_formatter ||= (key_formatter || DefaultKeyFormatter.new)
64
+ end
65
+
66
+ def key_formatter
67
+ self.class.respond_to?(:key_formatter) && self.class.key_formatter
68
+ end
69
+
70
+ class DefaultKeyFormatter
71
+ def unformat(method)
72
+ method.to_s
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module JsonApiClient
2
+ module Helpers
3
+ module URI
4
+ def encode_part(part)
5
+ Addressable::URI.encode_component(part, Addressable::URI::CharacterClasses::UNRESERVED)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module JsonApiClient
2
+ class Implementation
3
+ attr_reader :version, :meta
4
+
5
+ def initialize(data)
6
+ # If the version member is not present, clients should assume the server implements at least version 1.0 of the specification.
7
+ @version = data.fetch("version", "1.0")
8
+
9
+ @meta = MetaData.new(data.fetch("meta", {}))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ module JsonApiClient
2
+ class IncludedData
3
+ attr_reader :data
4
+
5
+ def initialize(result_set, data)
6
+ record_class = result_set.record_class
7
+ grouped_data = data.group_by{|datum| datum["type"]}
8
+ grouped_included_set = grouped_data.each_with_object({}) do |(type, records), h|
9
+ klass = Utils.compute_type(record_class, record_class.key_formatter.unformat(type).singularize.classify)
10
+ h[type] = records.map do |record|
11
+ params = klass.parser.parameters_from_resource(record)
12
+ klass.load(params).tap do |resource|
13
+ resource.last_result_set = result_set
14
+ end
15
+ end
16
+ end
17
+
18
+ if record_class.search_included_in_result_set
19
+ # deep_merge overrides the nested Arrays o_O
20
+ # {a: [1,2]}.deep_merge(a: [3,4]) # => {a: [3,4]}
21
+ grouped_included_set.merge!(result_set.group_by(&:type)) do |_, resources1, resources2|
22
+ resources1 + resources2
23
+ end
24
+ end
25
+
26
+ grouped_included_set.each do |type, resources|
27
+ grouped_included_set[type] = resources.index_by(&:id)
28
+ end
29
+
30
+ @data = grouped_included_set
31
+ end
32
+
33
+ def data_for(method_name, definition)
34
+ # If data is defined, pull the record from the included data
35
+ return nil unless data = definition["data"]
36
+
37
+ if data.is_a?(Array)
38
+ # has_many link
39
+ data.map(&method(:record_for)).compact
40
+ else
41
+ # has_one link
42
+ record_for(data)
43
+ end
44
+ end
45
+
46
+ def has_link?(name)
47
+ data.has_key?(name.to_s)
48
+ end
49
+
50
+ private
51
+
52
+ # should return a resource record of some type for this linked document
53
+ def record_for(link_def)
54
+ record = data[link_def["type"]]
55
+ record[link_def["id"]] if record
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,6 @@
1
+ module JsonApiClient
2
+ module Linking
3
+ autoload :Links, "json_api_client/linking/links"
4
+ autoload :TopLevelLinks, "json_api_client/linking/top_level_links"
5
+ end
6
+ end
@@ -0,0 +1,22 @@
1
+ module JsonApiClient
2
+ module Linking
3
+ class Links
4
+ include Helpers::DynamicAttributes
5
+
6
+ def initialize(links)
7
+ self.attributes = links
8
+ end
9
+
10
+ def present?
11
+ attributes.present?
12
+ end
13
+
14
+ protected
15
+
16
+ def set_attribute(name, value)
17
+ attributes[name] = value
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ module JsonApiClient
2
+ module Linking
3
+ class TopLevelLinks
4
+
5
+ attr_reader :links, :record_class
6
+
7
+ def initialize(record_class, links)
8
+ @links = links
9
+ @record_class = record_class
10
+ end
11
+
12
+ def respond_to_missing?(method, include_private = false)
13
+ links.has_key?(method.to_s) || super
14
+ end
15
+
16
+ def method_missing(method, *args)
17
+ if respond_to_missing?(method)
18
+ fetch_link(method)
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def link_url_for(link_name)
25
+ link_definition = links.fetch(link_name.to_s)
26
+ if link_definition.is_a?(Hash)
27
+ link_definition["href"]
28
+ else
29
+ link_definition
30
+ end
31
+ end
32
+
33
+ def fetch_link(link_name)
34
+ return unless respond_to_missing?(link_name)
35
+ record_class.requestor.linked(link_url_for(link_name))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module JsonApiClient
2
+ class MetaData
3
+ include Helpers::DynamicAttributes
4
+
5
+ attr_accessor :record_class
6
+
7
+ def initialize(data, record_class = nil)
8
+ self.record_class = record_class
9
+ self.attributes = data
10
+ end
11
+
12
+ protected
13
+
14
+ def key_formatter
15
+ record_class && record_class.key_formatter
16
+ end
17
+
18
+ end
19
+ end