acfs 1.3.3 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +372 -0
- data/LICENSE +22 -0
- data/README.md +321 -0
- data/acfs.gemspec +38 -0
- data/lib/acfs.rb +51 -0
- data/lib/acfs/adapter/base.rb +26 -0
- data/lib/acfs/adapter/typhoeus.rb +82 -0
- data/lib/acfs/collection.rb +28 -0
- data/lib/acfs/collections/paginatable.rb +76 -0
- data/lib/acfs/configuration.rb +120 -0
- data/lib/acfs/errors.rb +147 -0
- data/lib/acfs/global.rb +101 -0
- data/lib/acfs/location.rb +76 -0
- data/lib/acfs/middleware/base.rb +24 -0
- data/lib/acfs/middleware/json.rb +31 -0
- data/lib/acfs/middleware/logger.rb +23 -0
- data/lib/acfs/middleware/msgpack.rb +32 -0
- data/lib/acfs/middleware/print.rb +23 -0
- data/lib/acfs/middleware/serializer.rb +41 -0
- data/lib/acfs/operation.rb +96 -0
- data/lib/acfs/request.rb +32 -0
- data/lib/acfs/request/callbacks.rb +54 -0
- data/lib/acfs/resource.rb +39 -0
- data/lib/acfs/resource/attributes.rb +270 -0
- data/lib/acfs/resource/attributes/base.rb +29 -0
- data/lib/acfs/resource/attributes/boolean.rb +39 -0
- data/lib/acfs/resource/attributes/date_time.rb +32 -0
- data/lib/acfs/resource/attributes/dict.rb +39 -0
- data/lib/acfs/resource/attributes/float.rb +33 -0
- data/lib/acfs/resource/attributes/integer.rb +29 -0
- data/lib/acfs/resource/attributes/list.rb +36 -0
- data/lib/acfs/resource/attributes/string.rb +26 -0
- data/lib/acfs/resource/attributes/uuid.rb +48 -0
- data/lib/acfs/resource/dirty.rb +37 -0
- data/lib/acfs/resource/initialization.rb +31 -0
- data/lib/acfs/resource/loadable.rb +35 -0
- data/lib/acfs/resource/locatable.rb +135 -0
- data/lib/acfs/resource/operational.rb +26 -0
- data/lib/acfs/resource/persistence.rb +258 -0
- data/lib/acfs/resource/query_methods.rb +266 -0
- data/lib/acfs/resource/service.rb +44 -0
- data/lib/acfs/resource/validation.rb +49 -0
- data/lib/acfs/response.rb +30 -0
- data/lib/acfs/response/formats.rb +27 -0
- data/lib/acfs/response/status.rb +33 -0
- data/lib/acfs/rspec.rb +13 -0
- data/lib/acfs/runner.rb +102 -0
- data/lib/acfs/service.rb +94 -0
- data/lib/acfs/service/middleware.rb +58 -0
- data/lib/acfs/service/middleware/stack.rb +65 -0
- data/lib/acfs/singleton_resource.rb +85 -0
- data/lib/acfs/stub.rb +199 -0
- data/lib/acfs/util.rb +22 -0
- data/lib/acfs/version.rb +16 -0
- data/lib/acfs/yard.rb +6 -0
- data/spec/acfs/adapter/typhoeus_spec.rb +55 -0
- data/spec/acfs/collection_spec.rb +157 -0
- data/spec/acfs/configuration_spec.rb +53 -0
- data/spec/acfs/global_spec.rb +140 -0
- data/spec/acfs/location_spec.rb +25 -0
- data/spec/acfs/middleware/json_spec.rb +79 -0
- data/spec/acfs/middleware/msgpack_spec.rb +62 -0
- data/spec/acfs/operation_spec.rb +12 -0
- data/spec/acfs/request/callbacks_spec.rb +48 -0
- data/spec/acfs/request_spec.rb +79 -0
- data/spec/acfs/resource/attributes/boolean_spec.rb +58 -0
- data/spec/acfs/resource/attributes/date_time_spec.rb +51 -0
- data/spec/acfs/resource/attributes/dict_spec.rb +77 -0
- data/spec/acfs/resource/attributes/float_spec.rb +61 -0
- data/spec/acfs/resource/attributes/integer_spec.rb +36 -0
- data/spec/acfs/resource/attributes/list_spec.rb +60 -0
- data/spec/acfs/resource/attributes/uuid_spec.rb +42 -0
- data/spec/acfs/resource/attributes_spec.rb +179 -0
- data/spec/acfs/resource/dirty_spec.rb +49 -0
- data/spec/acfs/resource/initialization_spec.rb +36 -0
- data/spec/acfs/resource/loadable_spec.rb +22 -0
- data/spec/acfs/resource/locatable_spec.rb +118 -0
- data/spec/acfs/resource/persistance_spec.rb +322 -0
- data/spec/acfs/resource/query_methods_spec.rb +548 -0
- data/spec/acfs/resource/validation_spec.rb +129 -0
- data/spec/acfs/response/formats_spec.rb +52 -0
- data/spec/acfs/response/status_spec.rb +71 -0
- data/spec/acfs/runner_spec.rb +95 -0
- data/spec/acfs/service/middleware_spec.rb +35 -0
- data/spec/acfs/service_spec.rb +48 -0
- data/spec/acfs/singleton_resource_spec.rb +17 -0
- data/spec/acfs/stub_spec.rb +345 -0
- data/spec/acfs_spec.rb +205 -0
- data/spec/fixtures/config.yml +14 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/hash.rb +11 -0
- data/spec/support/response.rb +12 -0
- data/spec/support/service.rb +92 -0
- data/spec/support/shared/find_callbacks.rb +50 -0
- metadata +159 -26
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# DateTime attribute type. Use it in your model as
|
7
|
+
# an attribute type:
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class User < Acfs::Resource
|
11
|
+
# attribute :name, :date_time
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class DateTime < Base
|
15
|
+
# @api public
|
16
|
+
#
|
17
|
+
# Cast given object to DateTime.
|
18
|
+
#
|
19
|
+
# @param [Object] value Object to cast.
|
20
|
+
# @return [DateTime] Casted object as DateTime.
|
21
|
+
#
|
22
|
+
def cast_value(value)
|
23
|
+
if value.blank?
|
24
|
+
nil
|
25
|
+
elsif !value.is_a?(::String) && value.respond_to?(:to_datetime)
|
26
|
+
value.to_datetime
|
27
|
+
else
|
28
|
+
::DateTime.iso8601 value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# Dict attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User
|
10
|
+
# include Acfs::Model
|
11
|
+
# attribute :opts, :dict
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class Dict < Base
|
15
|
+
# @api public
|
16
|
+
#
|
17
|
+
# Cast given object to a dict/hash.
|
18
|
+
#
|
19
|
+
# @param [Object] value Object to cast.
|
20
|
+
# @return [Hash] Casted object as hash.
|
21
|
+
# @raise [TypeError] If object cannot be casted to a hash.
|
22
|
+
#
|
23
|
+
def cast_value(value)
|
24
|
+
return {} if value.blank?
|
25
|
+
|
26
|
+
if value.is_a?(Hash)
|
27
|
+
value
|
28
|
+
elsif value.respond_to?(:serializable_hash)
|
29
|
+
value.serializable_hash
|
30
|
+
elsif value.respond_to?(:to_hash)
|
31
|
+
value.to_hash
|
32
|
+
elsif value.respond_to?(:to_h)
|
33
|
+
value.to_h
|
34
|
+
else
|
35
|
+
Hash(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# Float attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User < Acfs::Resource
|
10
|
+
# attribute :name, :float
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
class Float < Base
|
14
|
+
# @api public
|
15
|
+
#
|
16
|
+
# Cast given object to float.
|
17
|
+
#
|
18
|
+
# @param [Object] value Object to cast.
|
19
|
+
# @return [Float] Casted object as float.
|
20
|
+
#
|
21
|
+
def cast_value(value)
|
22
|
+
return 0.0 if value.blank?
|
23
|
+
|
24
|
+
case value
|
25
|
+
when ::Float then value
|
26
|
+
when 'Infinity' then ::Float::INFINITY
|
27
|
+
when '-Infinity' then -::Float::INFINITY
|
28
|
+
when 'NaN' then ::Float::NAN
|
29
|
+
else Float(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# Integer attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User < Acfs::Resource
|
10
|
+
# attribute :name, :integer
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
class Integer < Base
|
14
|
+
# @api public
|
15
|
+
#
|
16
|
+
# Cast given object to integer.
|
17
|
+
#
|
18
|
+
# @param [Object] value Object to cast.
|
19
|
+
# @return [Fixnum] Casted object as fixnum.
|
20
|
+
#
|
21
|
+
def cast_value(value)
|
22
|
+
if value.blank?
|
23
|
+
0
|
24
|
+
else
|
25
|
+
Integer(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# List attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User < Acfs::Resource
|
10
|
+
# attribute :name, :list
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
class List < Base
|
14
|
+
# @api public
|
15
|
+
#
|
16
|
+
# Cast given object to a list.
|
17
|
+
#
|
18
|
+
# @param [Object] value Object to cast.
|
19
|
+
# @return [Fixnum] Casted object as list.
|
20
|
+
# @raise [TypeError] If object cannot be casted to a list.
|
21
|
+
#
|
22
|
+
def cast_value(value)
|
23
|
+
return [] if value.blank?
|
24
|
+
|
25
|
+
if value.is_a?(::Array)
|
26
|
+
value
|
27
|
+
elsif value.respond_to?(:to_ary)
|
28
|
+
value.to_ary
|
29
|
+
elsif value.respond_to?(:to_a)
|
30
|
+
value.to_a
|
31
|
+
else
|
32
|
+
Array(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# String attribute type. Use it in your model as
|
7
|
+
# an attribute type:
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class User < Acfs::Resource
|
11
|
+
# attribute :name, :string
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class String < Base
|
15
|
+
# @api public
|
16
|
+
#
|
17
|
+
# Cast given object to string.
|
18
|
+
#
|
19
|
+
# @param [Object] value Object to cast.
|
20
|
+
# @return [String] Casted string.
|
21
|
+
#
|
22
|
+
def cast_value(value)
|
23
|
+
value.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# UUID attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User < Acfs::Resource
|
10
|
+
# attribute :id, :uuid
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
class UUID < Base
|
14
|
+
UUID_REGEXP = /[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}/i.freeze
|
15
|
+
|
16
|
+
# @api public
|
17
|
+
#
|
18
|
+
# Check if given object looks like a UUID, eg:
|
19
|
+
# `450b7a40-94ad-11e3-baa8-0800200c9a66`
|
20
|
+
# Valid UUIDs are 16 byte numbers represented as
|
21
|
+
# a hexadecimal string in five sub-groups seperated
|
22
|
+
# by a dash. Each group has to consist of a fixed
|
23
|
+
# number of hexadecimal digits:
|
24
|
+
# | Group | Digits |
|
25
|
+
# | -----:|:------ |
|
26
|
+
# | 1 | 8 |
|
27
|
+
# | 2 | 4 |
|
28
|
+
# | 3 | 4 |
|
29
|
+
# | 4 | 4 |
|
30
|
+
# | 5 | 12 |
|
31
|
+
#
|
32
|
+
# @param [Object] value Object to cast.
|
33
|
+
# @return [String] Casted object as UUID.
|
34
|
+
#
|
35
|
+
def cast_value(value)
|
36
|
+
if value.blank?
|
37
|
+
nil
|
38
|
+
elsif value.to_s =~ UUID_REGEXP
|
39
|
+
value
|
40
|
+
else
|
41
|
+
raise TypeError.new "Invalid UUID: `#{value}'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Lower-case alias for automatic type lookup
|
47
|
+
Uuid = UUID
|
48
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
#
|
5
|
+
# Thin wrapper around ActiveModel::Dirty
|
6
|
+
#
|
7
|
+
module Dirty
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include ActiveModel::Dirty
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def reset_changes
|
14
|
+
clear_changes_information
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def save!(**kwargs)
|
20
|
+
super(**kwargs).tap {|_| changes_applied }
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def loaded!
|
26
|
+
reset_changes
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
def write_raw_attribute(name, value, opts = {})
|
33
|
+
attribute_will_change!(name) if opts[:change].nil? || opts[:change]
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
#
|
5
|
+
# Initialization drop-in for pre-4.0 ActiveModel.
|
6
|
+
#
|
7
|
+
module Initialization
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
#
|
11
|
+
# Initializes a new model with the given `params`.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class User < Acfs::Resource
|
15
|
+
# attribute :name
|
16
|
+
# attribute :email, default: ->{ "#{name}@dom.tld" }
|
17
|
+
# attribute :age, :integer, default: 18
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# user = User.new({name: 'bob'})
|
21
|
+
# user.name # => "bob"
|
22
|
+
# user.email # => "bob@dom.tld"
|
23
|
+
# user.age # => 18
|
24
|
+
#
|
25
|
+
# @param attributes [Hash{Symbol => Object}] Attributes to set on resource.
|
26
|
+
#
|
27
|
+
def initialize(attributes = {})
|
28
|
+
write_attributes(attributes) if attributes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
# Provides method to check for loading state of resources.
|
5
|
+
# A resource that is created but not yet fetched will be loaded
|
6
|
+
# after running {Acfs::Global#run Acfs.run}.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# user = User.find 5
|
10
|
+
# user.loaded? # => false
|
11
|
+
# Acfs.run
|
12
|
+
# user.loaded? # => true
|
13
|
+
#
|
14
|
+
module Loadable
|
15
|
+
extend ActiveSupport::Concern
|
16
|
+
|
17
|
+
# @api public
|
18
|
+
#
|
19
|
+
# Check if model is loaded or if request is still queued.
|
20
|
+
#
|
21
|
+
# @return [Boolean] True if resource is loaded, false otherwise.
|
22
|
+
#
|
23
|
+
def loaded?
|
24
|
+
@loaded.nil? ? false : @loaded
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
# Mark model as loaded.
|
30
|
+
#
|
31
|
+
def loaded!
|
32
|
+
@loaded = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
# Provide methods for generation URLs for resources.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class User
|
8
|
+
# service AccountService # With base URL `http://acc.svr`
|
9
|
+
# end
|
10
|
+
# User.url # => "http://acc.svr/users"
|
11
|
+
# User.url(5) # => "http://acc.svr/users/5"
|
12
|
+
#
|
13
|
+
module Locatable
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
# @overload url(suffix)
|
18
|
+
# @deprecated
|
19
|
+
# Return URL for this class of resource. Given suffix
|
20
|
+
# will be appended.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# User.url # => "http://users.srv.org/users"
|
24
|
+
# User.url(5) # => "http://users.srv.org/users/5"
|
25
|
+
#
|
26
|
+
# @param suffix [String] Suffix to append to URL.
|
27
|
+
# @return [String] Generated URL.
|
28
|
+
#
|
29
|
+
# @overload url(opts = {})
|
30
|
+
# Return URL for this class of resources. Given options
|
31
|
+
# will be used to replace URL path arguments and to
|
32
|
+
# determine the operation action.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# User.url(id: 5, action: :read) # => "http://users.srv.org/users/5"
|
36
|
+
# User.url(action: :list) # => "http://users.srv.org/users"
|
37
|
+
#
|
38
|
+
# @param opts [Hash] Options.
|
39
|
+
# @option opts [Symbol] :action Operation action,
|
40
|
+
# usually `:list`, `:create`, `:read`, `:update` or`:delete`.
|
41
|
+
# @return [String] Generated URL.
|
42
|
+
#
|
43
|
+
def url(suffix = nil, **opts)
|
44
|
+
if suffix.is_a? Hash
|
45
|
+
opts = suffix
|
46
|
+
suffix = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
kwargs = {}
|
50
|
+
kwargs[:path] = opts[:path] if opts.key?(:path)
|
51
|
+
kwargs[:action] = opts.delete(:action) if opts.key?(:action)
|
52
|
+
kwargs[:action] = :list if suffix
|
53
|
+
|
54
|
+
url = location(**kwargs).build(opts.stringify_keys).str
|
55
|
+
url += "/#{suffix}" if suffix.to_s.present?
|
56
|
+
url
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return a location object able to build the URL for this
|
60
|
+
# resource and given action.
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# class Identity < ::Acfs::Resource
|
64
|
+
# service MyService, path: 'users/:user_id/identities'
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# location = Identity.location(action: :read)
|
68
|
+
# location.arguments
|
69
|
+
# => [:user_id, :id]
|
70
|
+
#
|
71
|
+
# location.raw_url
|
72
|
+
# => 'http://service/users/:user_id/identities/:id'
|
73
|
+
#
|
74
|
+
# location = Identity.location(action: :list)
|
75
|
+
# location.arguments
|
76
|
+
# => [:user_id]
|
77
|
+
#
|
78
|
+
# location.build(user_id: 42)
|
79
|
+
# => 'http://service/users/42/identities'
|
80
|
+
#
|
81
|
+
# @param opts [Hash] Options.
|
82
|
+
# @option opts [Symbol] :action Operation action,
|
83
|
+
# usually `:list`, `:create`, `:read`, `:update` or`:delete`.
|
84
|
+
#
|
85
|
+
# @return [Location] Location object.
|
86
|
+
#
|
87
|
+
def location(**opts)
|
88
|
+
service.location(self, **opts)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @api private
|
92
|
+
def location_default_path(action, path)
|
93
|
+
case action
|
94
|
+
when :list, :create
|
95
|
+
path
|
96
|
+
when :read, :update, :delete
|
97
|
+
"#{path}/:id"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return URL for this resource. Resource if will be appended
|
103
|
+
# as suffix if present.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# user.new.url # => "http://users.srv.org/users"
|
107
|
+
#
|
108
|
+
# user = User.find 5
|
109
|
+
# Acfs.run
|
110
|
+
# user.url # => "http://users.srv.org/users/5"
|
111
|
+
#
|
112
|
+
# @return [ String ] Generated URL.
|
113
|
+
# @see ClassMethods#url
|
114
|
+
#
|
115
|
+
def url(**opts)
|
116
|
+
return nil if need_primary_key? && !primary_key?
|
117
|
+
|
118
|
+
self.class.service
|
119
|
+
.location(self.class, **opts, action: :read)
|
120
|
+
.build(attributes).str
|
121
|
+
end
|
122
|
+
|
123
|
+
# @api private
|
124
|
+
# Return true if resource needs a primary key (id) for singular actions.
|
125
|
+
def need_primary_key?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
# @api private
|
130
|
+
# Return true if resource has a primary key (id) set.
|
131
|
+
def primary_key?
|
132
|
+
respond_to?(:id) && !id.nil?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|