fulfil_api 0.2.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc3d42e014ce01e2bca045883fcbe4b43a2f701fafd6c4252c7b06c1a9314acf
4
- data.tar.gz: e6e79edf8b831515f3a706358c5884384b1f925b150443b1627ae33905ead5c6
3
+ metadata.gz: 31bfb876f7d82e4d803cbacf7b3628f5acb66c086188e1c56c50a37dfe6cb6f4
4
+ data.tar.gz: db1418268779073af449b43f71ccc47a8a4ad1db92c3261b7d22d9821f5155e0
5
5
  SHA512:
6
- metadata.gz: 8694733643edbf5caa327f42fb5121b4557869358afaf916c4cdfe30e2a7854ba70b189b24d8f1886b41054f1eb36aa6ffc1bafbc4c378e0b2276fa4513aaebc
7
- data.tar.gz: ef3818c50ac4a8e09660a690a7d3a97e0775111ccbd1f35825969ab66ee1cb4003e5c2aec2f2b26ef5902a0e1ad07a5fb90bda99ad4b6c94b7bff4a8b57dbcc3
6
+ metadata.gz: 5cdefd3a083ef390084db3a04276c49c02243b0cf39b52f9c6146657f712f68251e3087f636d789978536f106d1a59f1b3c18f014ebc2c6330501654cc94a08a
7
+ data.tar.gz: 0522da7f4f12ebf1753df8512a716a7c496c11d9f0740ea599ecbfc742b7ef026ca4324769d6bba5f2d37ee16ee4cb963baa5d318d46ea9020538c390fe98e02
@@ -16,8 +16,6 @@ module FulfilApi
16
16
  # @param options [Hash, nil] An optional list of configuration options.
17
17
  # Each key in the hash should correspond to a configuration attribute.
18
18
  def initialize(options = {})
19
- @mutex = Mutex.new
20
-
21
19
  # Assigns the optional configuration options
22
20
  options.each_pair do |key, value|
23
21
  send(:"#{key}=", value) if respond_to?(:"#{key}=")
@@ -27,25 +25,6 @@ module FulfilApi
27
25
  set_default_options
28
26
  end
29
27
 
30
- # Provides thread-safe access to missing methods, allowing dynamic handling of configuration options.
31
- #
32
- # @param method [Symbol] The method name.
33
- # @param args [Array] The arguments passed to the method.
34
- # @param block [Proc] An optional block passed to the method.
35
- # @return [void]
36
- def method_missing(method, *args, &block)
37
- @mutex.synchronize { super }
38
- end
39
-
40
- # Ensures that the object responds correctly to methods handled by method_missing.
41
- #
42
- # @param method [Symbol] The method name.
43
- # @param include_private [Boolean] Whether to include private methods.
44
- # @return [Boolean] Whether the object responds to the method.
45
- def respond_to_missing?(method, include_private = false)
46
- @mutex.synchronize { super }
47
- end
48
-
49
28
  private
50
29
 
51
30
  # Sets the default options for the gem configuration.
@@ -61,13 +40,12 @@ module FulfilApi
61
40
  end
62
41
 
63
42
  @configuration = Configuration.new
64
- @configuration_mutex = Mutex.new
65
43
 
66
44
  # Provides thread-safe access to the gem's configuration.
67
45
  #
68
46
  # @return [Fulfil::Configuration] The current configuration object.
69
47
  def self.configuration
70
- @configuration_mutex.synchronize { @configuration }
48
+ Thread.current[:fulfil_api_configuration] ||= Configuration.new
71
49
  end
72
50
 
73
51
  # Allows the configuration of the gem in a thread-safe manner.
@@ -75,23 +53,25 @@ module FulfilApi
75
53
  # @yieldparam [Fulfil::Configuration] config The current configuration object.
76
54
  # @return [void]
77
55
  def self.configure
78
- @configuration_mutex.synchronize { yield(@configuration) }
56
+ yield(configuration)
79
57
  end
80
58
 
81
59
  # Overwrites the configuration with the newly provided configuration options.
82
60
  #
83
61
  # @param options [Hash, Fulfil::Configuration] A list of configuration options for the gem.
84
62
  # @return [Fulfil::Configuration] The updated configuration object.
85
- def self.configuration=(options_or_configuration)
86
- @configuration_mutex.synchronize do
87
- if options_or_configuration.is_a?(Hash)
88
- options_or_configuration.each_pair do |key, value|
89
- @configuration.send(:"#{key}=", value) if @configuration.respond_to?(:"#{key}=")
90
- end
91
- elsif options_or_configuration.is_a?(Configuration)
92
- @configuration = options_or_configuration
63
+ def self.configuration=(options_or_configuration) # rubocop:disable Metrics/MethodLength
64
+ Thread.current[:fulfil_api_configuration] =
65
+ case options_or_configuration
66
+ when Hash
67
+ config = Configuration.new
68
+ options_or_configuration.each { |key, value| config.send(:"#{key}=", value) }
69
+ config
70
+ when Configuration
71
+ options_or_configuration
72
+ else
73
+ raise ArgumentError, "Expected Hash or Configuration, got #{options_or_configuration.class} instead"
93
74
  end
94
- end
95
75
  end
96
76
 
97
77
  # Temporarily applies the provided configuration options within a block,
@@ -107,6 +87,6 @@ module FulfilApi
107
87
  yield
108
88
  ensure
109
89
  # Revert to the original configuration
110
- self.configuration = original_configuration
90
+ Thread.current[:fulfil_api_configuration] = original_configuration
111
91
  end
112
92
  end
@@ -22,11 +22,31 @@ module FulfilApi
22
22
  # @param name [String, Symbol] The attribute name
23
23
  # @param value [Any] The attribute value
24
24
  # @return [Hash] The resource attributes
25
- def assign_attribute(name, value)
25
+ def assign_attribute(name, value) # rubocop:disable Metrics/MethodLength
26
26
  attribute = build_attribute(name, value)
27
27
  attribute.deep_stringify_keys!
28
28
 
29
- @attributes = @attributes.deep_merge(attribute)
29
+ # NOTE: Fulfil will assign the ID of a nested resource to its own namespace.
30
+ # This leads to conflicts when we're trying to parse the returned fields
31
+ # from the API.
32
+ #
33
+ # To address this problem, we're manually handling these cases. We're dealing
34
+ # with a nested relation when one of the values is an integer and the other
35
+ # is an hash.
36
+ #
37
+ # @example a nested relation
38
+ #
39
+ # $ resource.assign_attributes({ "warehouse.name" => "Toronto", "warehouse" => 10 })
40
+ # => <FulfilApi::Resource @attributes={"warehouse" => { "id" => 10, "name" => "Toronto" }} />
41
+ @attributes = @attributes.deep_merge(attribute) do |_key, current_value, other_value|
42
+ if current_value.is_a?(Integer) && other_value.is_a?(Hash)
43
+ { "id" => current_value }.deep_merge(other_value)
44
+ elsif current_value.is_a?(Hash) && other_value.is_a?(Integer)
45
+ current_value.deep_merge({ "id" => other_value })
46
+ else
47
+ other_value
48
+ end
49
+ end
30
50
  end
31
51
 
32
52
  private
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FulfilApi
4
+ class Resource
5
+ module Comparable
6
+ def ==(other)
7
+ other.is_a?(FulfilApi::Resource) &&
8
+ other.hash == hash
9
+ end
10
+
11
+ def eql?(other)
12
+ self == other
13
+ end
14
+
15
+ def hash
16
+ @attributes.hash
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FulfilApi
4
+ class Resource
5
+ module Serializable
6
+ extend ActiveSupport::Concern
7
+
8
+ class_methods do
9
+ # Turns a JSON string into a {FulfilApi::Resource}.
10
+ #
11
+ # @note it's required to include the name of the model as part of the JSON
12
+ # string too. Otherwise, you will encounter a naming error when attempting
13
+ # to turn the JSON into a {FulfilApi::Resource}
14
+ #
15
+ # @param json [String] The JSONified data
16
+ # @param root_included [true, false] When using Rails, one can include
17
+ # @return [FulfilApi::Resource]
18
+ def from_json(json, root_included: false)
19
+ attributes = JSON.parse(json)
20
+ attributes = attributes.values.first if root_included
21
+
22
+ new(attributes)
23
+ end
24
+ end
25
+
26
+ # Overwrites the default {#as_json} method because {ActiveModel} will nest
27
+ # the attributes when the model is transformed to JSON.
28
+ #
29
+ # @param options [Hash, nil] An optional list of options
30
+ # @return [Hash] A set of attributes available to be JSONified.
31
+ def as_json(options = nil)
32
+ # NOTE: We're including the model name by default. Otherwise, we can't use
33
+ # the {.from_json} method to parse it when reading from JSON.
34
+ hash = to_h.merge("model_name" => @model_name)
35
+
36
+ case options
37
+ in { root: }
38
+ { root => hash }
39
+ else
40
+ hash
41
+ end
42
+ end
43
+
44
+ # Turns the {Resource} into a JSON object.
45
+ #
46
+ # @param options [Hash, nil] An optional list of options
47
+ # @return [String] The JSONified resource data
48
+ def to_json(options = nil)
49
+ as_json(options).to_json
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,7 +5,9 @@ module FulfilApi
5
5
  # endpoints of Fulfil.
6
6
  class Resource
7
7
  include AttributeAssignable
8
+ include Comparable
8
9
  include Persistable
10
+ include Serializable
9
11
 
10
12
  # The model name is required to be able to build the API endpoint to
11
13
  # perform the search/read/count HTTP requests.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FulfilApi
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fulfil_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Vermaas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-15 00:00:00.000000000 Z
11
+ date: 2025-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -95,8 +95,10 @@ files:
95
95
  - lib/fulfil_api/resource.rb
96
96
  - lib/fulfil_api/resource/attribute_assignable.rb
97
97
  - lib/fulfil_api/resource/attribute_type.rb
98
+ - lib/fulfil_api/resource/comparable.rb
98
99
  - lib/fulfil_api/resource/errors.rb
99
100
  - lib/fulfil_api/resource/persistable.rb
101
+ - lib/fulfil_api/resource/serializable.rb
100
102
  - lib/fulfil_api/test_helper.rb
101
103
  - lib/fulfil_api/version.rb
102
104
  - sig/fulfil_api.rbs