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
data/lib/acfs/request.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'acfs/request/callbacks'
|
4
|
+
|
5
|
+
module Acfs
|
6
|
+
# Encapsulate all data required to make up a request to the
|
7
|
+
# underlaying http library.
|
8
|
+
#
|
9
|
+
class Request
|
10
|
+
attr_accessor :body, :format
|
11
|
+
attr_reader :url, :headers, :params, :data, :method, :operation
|
12
|
+
|
13
|
+
include Request::Callbacks
|
14
|
+
def initialize(url, **options, &block)
|
15
|
+
@url = URI.parse(url.to_s).tap do |_url|
|
16
|
+
@data = options.delete(:data) || nil
|
17
|
+
@format = options.delete(:format) || :json
|
18
|
+
@headers = options.delete(:headers) || {}
|
19
|
+
@params = options.delete(:params) || {}
|
20
|
+
@method = options.delete(:method) || :get
|
21
|
+
end.to_s
|
22
|
+
|
23
|
+
@operation = options.delete(:operation) || nil
|
24
|
+
|
25
|
+
on_complete(&block) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
def data?
|
29
|
+
!data.nil?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs
|
4
|
+
class Request
|
5
|
+
# Module containing callback handling for Requests.
|
6
|
+
# Current the only callback type is `on_complete`:
|
7
|
+
#
|
8
|
+
# request = Request.new 'URL'
|
9
|
+
# request.on_complete { |response| ... }
|
10
|
+
#
|
11
|
+
module Callbacks
|
12
|
+
# Add a new `on_complete` callback for this request.
|
13
|
+
#
|
14
|
+
# @example Set on_complete.
|
15
|
+
# request.on_complete { |response| print response.body }
|
16
|
+
#
|
17
|
+
# @param [ Block ] block The callback block to execute.
|
18
|
+
#
|
19
|
+
# @yield [ Acfs::Response ]
|
20
|
+
#
|
21
|
+
# @return [ Acfs::Request ] The request itself.
|
22
|
+
#
|
23
|
+
def on_complete(&block)
|
24
|
+
callbacks.insert 0, block if block_given?
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return array of all callbacks.
|
29
|
+
#
|
30
|
+
# @return [ Array<Block> ] All callbacks.
|
31
|
+
#
|
32
|
+
def callbacks
|
33
|
+
@callbacks ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
# Trigger all callback for given response.
|
37
|
+
#
|
38
|
+
# @return [ Acfs::Request ] The request itself.
|
39
|
+
#
|
40
|
+
def complete!(response)
|
41
|
+
call_callback response, 0
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def call_callback(res, index)
|
48
|
+
return if index >= callbacks.size
|
49
|
+
|
50
|
+
callbacks[index].call(res, proc {|bres| call_callback bres, index + 1 })
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
# @api public
|
6
|
+
#
|
7
|
+
class Acfs::Resource
|
8
|
+
require 'acfs/resource/initialization'
|
9
|
+
require 'acfs/resource/attributes'
|
10
|
+
require 'acfs/resource/dirty'
|
11
|
+
require 'acfs/resource/loadable'
|
12
|
+
require 'acfs/resource/locatable'
|
13
|
+
require 'acfs/resource/operational'
|
14
|
+
require 'acfs/resource/persistence'
|
15
|
+
require 'acfs/resource/query_methods'
|
16
|
+
require 'acfs/resource/service'
|
17
|
+
require 'acfs/resource/validation'
|
18
|
+
|
19
|
+
if ActiveModel::VERSION::MAJOR >= 4
|
20
|
+
include ActiveModel::Model
|
21
|
+
else
|
22
|
+
extend ActiveModel::Naming
|
23
|
+
extend ActiveModel::Translation
|
24
|
+
include ActiveModel::Conversion
|
25
|
+
include ActiveModel::Validations
|
26
|
+
end
|
27
|
+
|
28
|
+
include Initialization
|
29
|
+
|
30
|
+
include Attributes
|
31
|
+
include Loadable
|
32
|
+
include Persistence
|
33
|
+
include Locatable
|
34
|
+
include Operational
|
35
|
+
include QueryMethods
|
36
|
+
include Service
|
37
|
+
include Dirty
|
38
|
+
include Validation
|
39
|
+
end
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Acfs::Resource
|
4
|
+
#
|
5
|
+
# = Acfs Attributes
|
6
|
+
#
|
7
|
+
# Allows to specify attributes of a class with default
|
8
|
+
# values and type safety.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# class User < Acfs::Resource
|
12
|
+
# attribute :name, :string, default: 'Anon'
|
13
|
+
# attribute :age, :integer
|
14
|
+
# attribute :special, My::Special::Type
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# For each attribute a setter and getter will be created and values will be
|
18
|
+
# type casted when set.
|
19
|
+
#
|
20
|
+
module Attributes
|
21
|
+
extend ActiveSupport::Concern
|
22
|
+
include ActiveModel::AttributeMethods
|
23
|
+
|
24
|
+
# @api public
|
25
|
+
#
|
26
|
+
# Write default attributes defined in resource class.
|
27
|
+
#
|
28
|
+
# @see #write_attributes
|
29
|
+
# @see ClassMethods#attributes
|
30
|
+
#
|
31
|
+
def initialize(*attrs)
|
32
|
+
write_attributes self.class.attributes
|
33
|
+
reset_changes
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api public
|
38
|
+
#
|
39
|
+
# Returns ActiveModel compatible list of attributes and values.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# class User < Acfs::Resource
|
43
|
+
# attribute :name, type: String, default: 'Anon'
|
44
|
+
# end
|
45
|
+
# user = User.new(name: 'John')
|
46
|
+
# user.attributes # => { "name" => "John" }
|
47
|
+
#
|
48
|
+
# @return [HashWithIndifferentAccess{Symbol => Object}]
|
49
|
+
# Attributes and their values.
|
50
|
+
#
|
51
|
+
# rubocop:disable Naming/MemoizedInstanceVariableName
|
52
|
+
def attributes
|
53
|
+
@_attrs ||= HashWithIndifferentAccess.new
|
54
|
+
end
|
55
|
+
# rubocop:enable Naming/MemoizedInstanceVariableName
|
56
|
+
|
57
|
+
# @api public
|
58
|
+
#
|
59
|
+
# Update all attributes with given hash. Attribute values will be casted
|
60
|
+
# to defined attribute type.
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# user.attributes = { :name => 'Adam' }
|
64
|
+
# user.name # => 'Adam'
|
65
|
+
#
|
66
|
+
# @param [Hash{String, Symbol => Object}, #each{|key, value|}]
|
67
|
+
# Attributes to set in resource.
|
68
|
+
# @see #write_attributes Delegates attributes hash to {#write_attributes}.
|
69
|
+
#
|
70
|
+
def attributes=(attributes)
|
71
|
+
write_attributes(attributes)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api public
|
75
|
+
#
|
76
|
+
# Read an attribute from instance variable.
|
77
|
+
#
|
78
|
+
# @param [Symbol, String] name Attribute name.
|
79
|
+
# @return [Object] Attribute value.
|
80
|
+
#
|
81
|
+
def read_attribute(name)
|
82
|
+
attributes[name.to_s]
|
83
|
+
end
|
84
|
+
|
85
|
+
# @api public
|
86
|
+
#
|
87
|
+
# Write a hash of attributes and values.
|
88
|
+
#
|
89
|
+
# If attribute value is a `Proc` it will be evaluated in the context
|
90
|
+
# of the resource after all non-proc attribute values are set. Values
|
91
|
+
# will be casted to defined attribute type.
|
92
|
+
#
|
93
|
+
# The behavior is used to apply default attributes from resource
|
94
|
+
# class definition.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# user.write_attributes name: 'john', email: ->{ "#{name}@example.org" }
|
98
|
+
# user.name # => 'john'
|
99
|
+
# user.email # => 'john@example.org'
|
100
|
+
#
|
101
|
+
# @param [Hash{String, Symbol => Object, Proc}, #each{|key, value|}]
|
102
|
+
# Attributes to write.
|
103
|
+
#
|
104
|
+
# @see #write_attribute Delegates attribute values to `#write_attribute`.
|
105
|
+
#
|
106
|
+
def write_attributes(attributes, **opts)
|
107
|
+
unless attributes.respond_to?(:each) && attributes.respond_to?(:keys)
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
|
111
|
+
if opts.fetch(:unknown, :ignore) == :raise &&
|
112
|
+
(attributes.keys.map(&:to_s) - self.class.attributes.keys).any?
|
113
|
+
missing = attributes.keys - self.class.attributes.keys
|
114
|
+
missing.map!(&:inspect)
|
115
|
+
raise ArgumentError.new "Unknown attributes: #{missing.join(', ')}"
|
116
|
+
end
|
117
|
+
|
118
|
+
procs = {}
|
119
|
+
|
120
|
+
attributes.each do |key, _|
|
121
|
+
if attributes[key].is_a? Proc
|
122
|
+
procs[key] = attributes[key]
|
123
|
+
else
|
124
|
+
write_local_attribute(key, attributes[key], **opts)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
procs.each do |key, proc|
|
129
|
+
write_local_attribute(key, instance_exec(&proc), **opts)
|
130
|
+
end
|
131
|
+
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
# @api private
|
136
|
+
#
|
137
|
+
# Check if a public getter for attribute exists that should be called to
|
138
|
+
# write it or of {#write_attribute} should be called directly. This is
|
139
|
+
# necessary as {#write_attribute} should go though setters but can also
|
140
|
+
# handle unknown attribute that will not have a generated setter method.
|
141
|
+
#
|
142
|
+
def write_local_attribute(name, value, opts = {})
|
143
|
+
method = "#{name}="
|
144
|
+
if respond_to? method, true
|
145
|
+
public_send method, value
|
146
|
+
else
|
147
|
+
write_attribute name, value, opts
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @api public
|
152
|
+
#
|
153
|
+
# Write single attribute with given value. Value will be casted
|
154
|
+
# to defined attribute type.
|
155
|
+
#
|
156
|
+
# @param [String, Symbol] name Attribute name.
|
157
|
+
# @param [Object] value Value to write.
|
158
|
+
# @raise [ArgumentError] If no attribute with given name is defined.
|
159
|
+
#
|
160
|
+
def write_attribute(name, value, opts = {})
|
161
|
+
attr_type = self.class.defined_attributes[name.to_s]
|
162
|
+
if attr_type
|
163
|
+
write_raw_attribute name, attr_type.cast(value), opts
|
164
|
+
else
|
165
|
+
write_raw_attribute name, value, opts
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
#
|
171
|
+
# Write an attribute without checking type or existence or casting
|
172
|
+
# value to attributes type. Value be stored in an instance variable
|
173
|
+
# named after attribute name.
|
174
|
+
#
|
175
|
+
# @param [String, Symbol] name Attribute name.
|
176
|
+
# @param [Object] value Attribute value.
|
177
|
+
#
|
178
|
+
def write_raw_attribute(name, value, _ = {})
|
179
|
+
attributes[name.to_s] = value
|
180
|
+
end
|
181
|
+
|
182
|
+
module ClassMethods
|
183
|
+
ATTR_CLASS_BASE = '::Acfs::Resource::Attributes'
|
184
|
+
|
185
|
+
#
|
186
|
+
# @api public
|
187
|
+
#
|
188
|
+
# Define a model attribute by name and type. Will create getter and
|
189
|
+
# setter for given attribute name. Existing methods will be overridden.
|
190
|
+
#
|
191
|
+
# Available types can be found in `Acfs::Model::Attributes::*`.
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# class User < Acfs::Resource
|
195
|
+
# attribute :name, :string, default: 'Anon'
|
196
|
+
# attribute :email, :string, default: lambda{ "#{name}@example.org"}
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# @param [#to_sym] name Attribute name.
|
200
|
+
# @param [Symbol, String, Class] type Attribute
|
201
|
+
# type identifier or type class.
|
202
|
+
#
|
203
|
+
def attribute(name, type, **opts)
|
204
|
+
if type.is_a?(Symbol) || type.is_a?(String)
|
205
|
+
type = "#{ATTR_CLASS_BASE}::#{type.to_s.classify}".constantize
|
206
|
+
end
|
207
|
+
|
208
|
+
define_attribute(name.to_sym, type, **opts)
|
209
|
+
end
|
210
|
+
|
211
|
+
# @api public
|
212
|
+
#
|
213
|
+
# Return list of possible attributes and default
|
214
|
+
# values for this model class.
|
215
|
+
#
|
216
|
+
# @example
|
217
|
+
# class User < Acfs::Resource
|
218
|
+
# attribute :name, :string
|
219
|
+
# attribute :age, :integer, default: 25
|
220
|
+
# end
|
221
|
+
# User.attributes # => { "name": nil, "age": 25 }
|
222
|
+
#
|
223
|
+
# @return [Hash{String => Object, Proc}]
|
224
|
+
# Attributes with default values.
|
225
|
+
#
|
226
|
+
def attributes
|
227
|
+
defined_attributes.each_with_object({}) do |(key, attr), hash|
|
228
|
+
hash[key] = attr.default_value
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def defined_attributes
|
233
|
+
if superclass.respond_to?(:defined_attributes)
|
234
|
+
superclass.defined_attributes.merge(local_attributes)
|
235
|
+
else
|
236
|
+
local_attributes
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
def local_attributes
|
243
|
+
@local_attributes ||= {}
|
244
|
+
end
|
245
|
+
|
246
|
+
def define_attribute(name, type, **opts)
|
247
|
+
name = name.to_s
|
248
|
+
attribute = type.new(**opts)
|
249
|
+
|
250
|
+
local_attributes[name] = attribute
|
251
|
+
define_attribute_method name
|
252
|
+
|
253
|
+
send :define_method, name do
|
254
|
+
read_attribute name
|
255
|
+
end
|
256
|
+
|
257
|
+
send :define_method, :"#{name}=" do |value|
|
258
|
+
write_attribute name, value
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Load attribute type classes.
|
266
|
+
#
|
267
|
+
Dir[File.join(__dir__, 'attributes/*.rb')].sort.each do |path|
|
268
|
+
filename = File.basename(path)
|
269
|
+
require "acfs/resource/attributes/#{filename}"
|
270
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
class Base
|
5
|
+
attr_reader :default
|
6
|
+
|
7
|
+
def initialize(default: nil)
|
8
|
+
@default = default
|
9
|
+
end
|
10
|
+
|
11
|
+
def cast(value)
|
12
|
+
cast_value(value) unless value.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_value
|
16
|
+
if default.respond_to? :call
|
17
|
+
default
|
18
|
+
else
|
19
|
+
cast default
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def cast_value(_value)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Acfs::Resource::Attributes
|
4
|
+
# @api public
|
5
|
+
#
|
6
|
+
# Boolean attribute type. Use it in your model as an attribute type:
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class User < Acfs::Resource
|
10
|
+
# attribute :name, :boolean
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# Given objects will be converted to string. The following strings
|
14
|
+
# are considered true, everything else false:
|
15
|
+
#
|
16
|
+
# true, on, yes
|
17
|
+
#
|
18
|
+
class Boolean < Base
|
19
|
+
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 'no', 'NO'].to_set
|
20
|
+
|
21
|
+
# @api public
|
22
|
+
#
|
23
|
+
# Cast given object to boolean.
|
24
|
+
#
|
25
|
+
# @param [Object] value Object to cast.
|
26
|
+
# @return [TrueClass, FalseClass] Casted boolean.
|
27
|
+
#
|
28
|
+
def cast_value(value)
|
29
|
+
return true if value == true
|
30
|
+
return false if value == false
|
31
|
+
|
32
|
+
if value.blank?
|
33
|
+
nil
|
34
|
+
else
|
35
|
+
!FALSE_VALUES.include?(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|