carwow-json_api_client 1.19.0

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