faraday 0.16.0 → 0.17.4

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +358 -18
  5. data/Rakefile +13 -0
  6. data/lib/faraday/adapter/em_http.rb +97 -140
  7. data/lib/faraday/adapter/em_http_ssl_patch.rb +17 -23
  8. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +15 -18
  9. data/lib/faraday/adapter/em_synchrony.rb +60 -104
  10. data/lib/faraday/adapter/excon.rb +55 -100
  11. data/lib/faraday/adapter/httpclient.rb +39 -61
  12. data/lib/faraday/adapter/net_http.rb +51 -103
  13. data/lib/faraday/adapter/net_http_persistent.rb +28 -49
  14. data/lib/faraday/adapter/patron.rb +35 -54
  15. data/lib/faraday/adapter/rack.rb +12 -28
  16. data/lib/faraday/adapter/test.rb +53 -86
  17. data/lib/faraday/adapter/typhoeus.rb +1 -4
  18. data/lib/faraday/adapter.rb +22 -36
  19. data/lib/faraday/autoload.rb +36 -47
  20. data/lib/faraday/connection.rb +179 -321
  21. data/lib/faraday/deprecate.rb +109 -0
  22. data/lib/faraday/error.rb +79 -21
  23. data/lib/faraday/middleware.rb +28 -4
  24. data/lib/faraday/options.rb +183 -32
  25. data/lib/faraday/parameters.rb +197 -4
  26. data/lib/faraday/rack_builder.rb +55 -66
  27. data/lib/faraday/request/authorization.rb +30 -42
  28. data/lib/faraday/request/basic_authentication.rb +7 -14
  29. data/lib/faraday/request/instrumentation.rb +27 -45
  30. data/lib/faraday/request/multipart.rb +48 -79
  31. data/lib/faraday/request/retry.rb +171 -197
  32. data/lib/faraday/request/token_authentication.rb +10 -15
  33. data/lib/faraday/request/url_encoded.rb +23 -41
  34. data/lib/faraday/request.rb +36 -68
  35. data/lib/faraday/response/logger.rb +69 -22
  36. data/lib/faraday/response/raise_error.rb +18 -36
  37. data/lib/faraday/response.rb +13 -20
  38. data/lib/faraday/upload_io.rb +67 -0
  39. data/lib/faraday/utils.rb +245 -28
  40. data/lib/faraday.rb +174 -93
  41. data/spec/faraday/deprecate_spec.rb +147 -0
  42. data/spec/faraday/error_spec.rb +102 -0
  43. data/spec/faraday/response/raise_error_spec.rb +106 -0
  44. data/spec/spec_helper.rb +105 -0
  45. data/test/adapters/default_test.rb +14 -0
  46. data/test/adapters/em_http_test.rb +30 -0
  47. data/test/adapters/em_synchrony_test.rb +32 -0
  48. data/test/adapters/excon_test.rb +30 -0
  49. data/test/adapters/httpclient_test.rb +34 -0
  50. data/test/adapters/integration.rb +263 -0
  51. data/test/adapters/logger_test.rb +136 -0
  52. data/test/adapters/net_http_persistent_test.rb +114 -0
  53. data/test/adapters/net_http_test.rb +79 -0
  54. data/test/adapters/patron_test.rb +40 -0
  55. data/test/adapters/rack_test.rb +38 -0
  56. data/test/adapters/test_middleware_test.rb +157 -0
  57. data/test/adapters/typhoeus_test.rb +38 -0
  58. data/test/authentication_middleware_test.rb +65 -0
  59. data/test/composite_read_io_test.rb +109 -0
  60. data/test/connection_test.rb +738 -0
  61. data/test/env_test.rb +268 -0
  62. data/test/helper.rb +75 -0
  63. data/test/live_server.rb +67 -0
  64. data/test/middleware/instrumentation_test.rb +88 -0
  65. data/test/middleware/retry_test.rb +282 -0
  66. data/test/middleware_stack_test.rb +260 -0
  67. data/test/multibyte.txt +1 -0
  68. data/test/options_test.rb +333 -0
  69. data/test/parameters_test.rb +157 -0
  70. data/test/request_middleware_test.rb +126 -0
  71. data/test/response_middleware_test.rb +72 -0
  72. data/test/strawberry.rb +2 -0
  73. data/test/utils_test.rb +98 -0
  74. metadata +50 -25
  75. data/lib/faraday/adapter_registry.rb +0 -28
  76. data/lib/faraday/dependency_loader.rb +0 -37
  77. data/lib/faraday/encoders/flat_params_encoder.rb +0 -94
  78. data/lib/faraday/encoders/nested_params_encoder.rb +0 -171
  79. data/lib/faraday/file_part.rb +0 -128
  80. data/lib/faraday/logging/formatter.rb +0 -92
  81. data/lib/faraday/middleware_registry.rb +0 -129
  82. data/lib/faraday/options/connection_options.rb +0 -22
  83. data/lib/faraday/options/env.rb +0 -181
  84. data/lib/faraday/options/proxy_options.rb +0 -28
  85. data/lib/faraday/options/request_options.rb +0 -21
  86. data/lib/faraday/options/ssl_options.rb +0 -59
  87. data/lib/faraday/param_part.rb +0 -53
  88. data/lib/faraday/utils/headers.rb +0 -139
  89. data/lib/faraday/utils/params_hash.rb +0 -61
  90. data/spec/external_adapters/faraday_specs_setup.rb +0 -14
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ # @param new_klass [Class] new Klass to use
5
+ #
6
+ # @return [Class] A modified version of new_klass that warns on
7
+ # usage about deprecation.
8
+ # @see Faraday::Deprecate
9
+ module DeprecatedClass
10
+ def self.proxy_class(origclass, ver = '1.0')
11
+ proxy = Class.new(origclass) do
12
+ const_set("ORIG_CLASS", origclass)
13
+
14
+ class << self
15
+ extend Faraday::Deprecate
16
+
17
+ def ===(other)
18
+ (superclass == const_get("ORIG_CLASS") && other.is_a?(superclass)) || super
19
+ end
20
+ end
21
+ end
22
+ proxy.singleton_class.send(:deprecate, :new, "#{origclass}.new", ver)
23
+ proxy.singleton_class.send(:deprecate, :inherited, origclass.name, ver)
24
+ proxy
25
+ end
26
+ end
27
+
28
+ # Deprecation using semver instead of date, based on Gem::Deprecate
29
+ # Provides a single method +deprecate+ to be used to declare when
30
+ # something is going away.
31
+ #
32
+ # class Legacy
33
+ # def self.klass_method
34
+ # # ...
35
+ # end
36
+ #
37
+ # def instance_method
38
+ # # ...
39
+ # end
40
+ #
41
+ # extend Faraday::Deprecate
42
+ # deprecate :instance_method, "X.z", '1.0'
43
+ #
44
+ # class << self
45
+ # extend Faraday::Deprecate
46
+ # deprecate :klass_method, :none, '1.0'
47
+ # end
48
+ # end
49
+ module Deprecate
50
+ def self.skip # :nodoc:
51
+ @skip ||= begin
52
+ case ENV['FARADAY_DEPRECATE'].to_s.downcase
53
+ when '1', 'warn' then :warn
54
+ else :skip
55
+ end
56
+ end
57
+ @skip == :skip
58
+ end
59
+
60
+ def self.skip=(value) # :nodoc:
61
+ @skip = value ? :skip : :warn
62
+ end
63
+
64
+ # Temporarily turn off warnings. Intended for tests only.
65
+ def skip_during
66
+ original = Faraday::Deprecate.skip
67
+ Faraday::Deprecate.skip, = true
68
+ yield
69
+ ensure
70
+ Faraday::Deprecate.skip = original
71
+ end
72
+
73
+ # Simple deprecation method that deprecates +name+ by wrapping it up
74
+ # in a dummy method. It warns on each call to the dummy method
75
+ # telling the user of +repl+ (unless +repl+ is :none) and the
76
+ # semver that it is planned to go away.
77
+ # @param name [Symbol] the method symbol to deprecate
78
+ # @param repl [#to_s, :none] the replacement to use, when `:none` it will
79
+ # alert the user that no replacemtent is present.
80
+ # @param ver [String] the semver the method will be removed.
81
+ def deprecate(name, repl, ver)
82
+ class_eval do
83
+ gem_ver = Gem::Version.new(ver)
84
+ old = "_deprecated_#{name}"
85
+ alias_method old, name
86
+ define_method name do |*args, &block|
87
+ mod = is_a? Module
88
+ target = mod ? "#{self}." : "#{self.class}#"
89
+ target_message = if name == :inherited
90
+ "Inheriting #{self}"
91
+ else
92
+ "#{target}#{name}"
93
+ end
94
+
95
+ msg = [
96
+ "NOTE: #{target_message} is deprecated",
97
+ repl == :none ? ' with no replacement' : "; use #{repl} instead. ",
98
+ "It will be removed in or after version #{gem_ver}",
99
+ "\n#{target}#{name} called from #{Gem.location_of_caller.join(':')}"
100
+ ]
101
+ warn "#{msg.join}." unless Faraday::Deprecate.skip
102
+ send old, *args, &block
103
+ end
104
+ end
105
+ end
106
+
107
+ module_function :deprecate, :skip_during
108
+ end
109
+ end
data/lib/faraday/error.rb CHANGED
@@ -1,23 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday/deprecate'
4
+
5
+ # Faraday namespace.
3
6
  module Faraday
4
7
  # Faraday error base class.
5
8
  class Error < StandardError
6
9
  attr_reader :response, :wrapped_exception
7
10
 
8
11
  def initialize(exc, response = nil)
9
- @wrapped_exception = nil
10
- @response = response
11
-
12
- if exc.respond_to?(:backtrace)
13
- super(exc.message)
14
- @wrapped_exception = exc
15
- elsif exc.respond_to?(:each_key)
16
- super("the server responded with status #{exc[:status]}")
17
- @response = exc
18
- else
19
- super(exc.to_s)
20
- end
12
+ @wrapped_exception = nil unless defined?(@wrapped_exception)
13
+ @response = nil unless defined?(@response)
14
+ super(exc_msg_and_response!(exc, response))
21
15
  end
22
16
 
23
17
  def backtrace
@@ -29,12 +23,44 @@ module Faraday
29
23
  end
30
24
 
31
25
  def inspect
32
- inner = +''
33
- inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
34
- inner << " response=#{@response.inspect}" if @response
35
- inner << " #{super}" if inner.empty?
26
+ inner = ''
27
+ inner += " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
28
+ inner += " response=#{@response.inspect}" if @response
29
+ inner += " #{super}" if inner.empty?
36
30
  %(#<#{self.class}#{inner}>)
37
31
  end
32
+
33
+ protected
34
+
35
+ # Pulls out potential parent exception and response hash, storing them in
36
+ # instance variables.
37
+ # exc - Either an Exception, a string message, or a response hash.
38
+ # response - Hash
39
+ # :status - Optional integer HTTP response status
40
+ # :headers - String key/value hash of HTTP response header
41
+ # values.
42
+ # :body - Optional string HTTP response body.
43
+ #
44
+ # If a subclass has to call this, then it should pass a string message
45
+ # to `super`. See NilStatusError.
46
+ def exc_msg_and_response!(exc, response = nil)
47
+ if @response.nil? && @wrapped_exception.nil?
48
+ @wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
49
+ return msg
50
+ end
51
+
52
+ exc.to_s
53
+ end
54
+
55
+ # Pulls out potential parent exception and response hash.
56
+ def exc_msg_and_response(exc, response = nil)
57
+ return [exc, exc.message, response] if exc.respond_to?(:backtrace)
58
+
59
+ return [nil, "the server responded with status #{exc[:status]}", exc] \
60
+ if exc.respond_to?(:each_key)
61
+
62
+ [nil, exc.to_s, response]
63
+ end
38
64
  end
39
65
 
40
66
  # Faraday client error class. Represents 4xx status responses.
@@ -74,27 +100,59 @@ module Faraday
74
100
  end
75
101
 
76
102
  # A unified client error for timeouts.
77
- class TimeoutError < ServerError
103
+ class TimeoutError < ClientError
78
104
  def initialize(exc = 'timeout', response = nil)
79
105
  super(exc, response)
80
106
  end
81
107
  end
82
108
 
109
+ # Raised by Faraday::Response::RaiseError in case of a nil status in response.
110
+ class NilStatusError < ServerError
111
+ def initialize(exc, response = nil)
112
+ exc_msg_and_response!(exc, response)
113
+ @response = unwrap_resp!(@response)
114
+ super('http status could not be derived from the server response')
115
+ end
116
+
117
+ private
118
+
119
+ extend Faraday::Deprecate
120
+
121
+ def unwrap_resp(resp)
122
+ if inner = (resp.keys.size == 1 && resp[:response])
123
+ return unwrap_resp(inner)
124
+ end
125
+
126
+ resp
127
+ end
128
+
129
+ alias_method :unwrap_resp!, :unwrap_resp
130
+ deprecate('unwrap_resp', nil, '1.0')
131
+ end
132
+
83
133
  # A unified error for failed connections.
84
- class ConnectionFailed < Error
134
+ class ConnectionFailed < ClientError
85
135
  end
86
136
 
87
137
  # A unified client error for SSL errors.
88
- class SSLError < Error
138
+ class SSLError < ClientError
89
139
  end
90
140
 
91
141
  # Raised by FaradayMiddleware::ResponseMiddleware
92
- class ParsingError < Error
142
+ class ParsingError < ClientError
93
143
  end
94
144
 
95
145
  # Exception used to control the Retry middleware.
96
146
  #
97
147
  # @see Faraday::Request::Retry
98
- class RetriableResponse < Error
148
+ class RetriableResponse < ClientError
149
+ end
150
+
151
+ [:ClientError, :ConnectionFailed, :ResourceNotFound,
152
+ :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
153
+ Error.const_set(
154
+ const,
155
+ DeprecatedClass.proxy_class(Faraday.const_get(const))
156
+ )
99
157
  end
100
158
  end
@@ -1,10 +1,34 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Faraday
4
- # Middleware is the basic base class of any Faraday middleware.
5
2
  class Middleware
6
3
  extend MiddlewareRegistry
7
- extend DependencyLoader
4
+
5
+ class << self
6
+ attr_accessor :load_error
7
+ private :load_error=
8
+ end
9
+
10
+ self.load_error = nil
11
+
12
+ # Executes a block which should try to require and reference dependent libraries
13
+ def self.dependency(lib = nil)
14
+ lib ? require(lib) : yield
15
+ rescue LoadError, NameError => error
16
+ self.load_error = error
17
+ end
18
+
19
+ def self.new(*)
20
+ raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
21
+ super
22
+ end
23
+
24
+ def self.loaded?
25
+ load_error.nil?
26
+ end
27
+
28
+ def self.inherited(subclass)
29
+ super
30
+ subclass.send(:load_error=, self.load_error)
31
+ end
8
32
 
9
33
  def initialize(app = nil)
10
34
  @app = app
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Faraday
4
2
  # Subclasses Struct with some special helpers for converting from a Hash to
5
3
  # a Struct.
@@ -12,7 +10,6 @@ module Faraday
12
10
  # Public
13
11
  def each
14
12
  return to_enum(:each) unless block_given?
15
-
16
13
  members.each do |key|
17
14
  yield(key.to_sym, send(key))
18
15
  end
@@ -30,7 +27,7 @@ module Faraday
30
27
  new_value = value
31
28
  end
32
29
 
33
- send("#{key}=", new_value) unless new_value.nil?
30
+ self.send("#{key}=", new_value) unless new_value.nil?
34
31
  end
35
32
  self
36
33
  end
@@ -50,14 +47,10 @@ module Faraday
50
47
  # Public
51
48
  def merge!(other)
52
49
  other.each do |key, other_value|
53
- self_value = send(key)
50
+ self_value = self.send(key)
54
51
  sub_options = self.class.options_for(key)
55
- new_value = if self_value && sub_options && other_value
56
- self_value.merge(other_value)
57
- else
58
- other_value
59
- end
60
- send("#{key}=", new_value) unless new_value.nil?
52
+ new_value = (self_value && sub_options && other_value) ? self_value.merge(other_value) : other_value
53
+ self.send("#{key}=", new_value) unless new_value.nil?
61
54
  end
62
55
  self
63
56
  end
@@ -76,7 +69,7 @@ module Faraday
76
69
  def fetch(key, *args)
77
70
  unless symbolized_key_set.include?(key.to_sym)
78
71
  key_setter = "#{key}="
79
- if !args.empty?
72
+ if args.size > 0
80
73
  send(key_setter, args.first)
81
74
  elsif block_given?
82
75
  send(key_setter, yield(key))
@@ -105,7 +98,6 @@ module Faraday
105
98
  # Public
106
99
  def each_key
107
100
  return to_enum(:each_key) unless block_given?
108
-
109
101
  keys.each do |key|
110
102
  yield(key)
111
103
  end
@@ -121,7 +113,6 @@ module Faraday
121
113
  # Public
122
114
  def each_value
123
115
  return to_enum(:each_value) unless block_given?
124
-
125
116
  values.each do |value|
126
117
  yield(value)
127
118
  end
@@ -151,9 +142,9 @@ module Faraday
151
142
  value = send(member)
152
143
  values << "#{member}=#{value.inspect}" if value
153
144
  end
154
- values = values.empty? ? '(empty)' : values.join(', ')
145
+ values = values.empty? ? ' (empty)' : (' ' << values.join(", "))
155
146
 
156
- %(#<#{self.class} #{values}>)
147
+ %(#<#{self.class}#{values}>)
157
148
  end
158
149
 
159
150
  # Internal
@@ -172,10 +163,6 @@ module Faraday
172
163
  end
173
164
 
174
165
  def self.memoized(key, &block)
175
- unless block_given?
176
- raise ArgumentError, '#memoized must be called with a block'
177
- end
178
-
179
166
  memoized_attributes[key.to_sym] = block
180
167
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
181
168
  def #{key}() self[:#{key}]; end
@@ -188,7 +175,7 @@ module Faraday
188
175
 
189
176
  def [](key)
190
177
  key = key.to_sym
191
- if (method = self.class.memoized_attributes[key])
178
+ if method = self.class.memoized_attributes[key]
192
179
  super(key) || (self[key] = instance_eval(&method))
193
180
  else
194
181
  super
@@ -196,7 +183,7 @@ module Faraday
196
183
  end
197
184
 
198
185
  def symbolized_key_set
199
- @symbolized_key_set ||= Set.new(keys.map(&:to_sym))
186
+ @symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym })
200
187
  end
201
188
 
202
189
  def self.inherited(subclass)
@@ -207,16 +194,180 @@ module Faraday
207
194
 
208
195
  def self.fetch_error_class
209
196
  @fetch_error_class ||= if Object.const_defined?(:KeyError)
210
- ::KeyError
211
- else
212
- ::IndexError
213
- end
197
+ ::KeyError
198
+ else
199
+ ::IndexError
200
+ end
214
201
  end
215
202
  end
216
- end
217
203
 
218
- require 'faraday/options/request_options'
219
- require 'faraday/options/ssl_options'
220
- require 'faraday/options/proxy_options'
221
- require 'faraday/options/connection_options'
222
- require 'faraday/options/env'
204
+ class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
205
+ :timeout, :open_timeout, :write_timeout, :boundary, :oauth, :context)
206
+
207
+ def []=(key, value)
208
+ if key && key.to_sym == :proxy
209
+ super(key, value ? ProxyOptions.from(value) : nil)
210
+ else
211
+ super(key, value)
212
+ end
213
+ end
214
+ end
215
+
216
+ class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
217
+ :cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth,
218
+ :version, :min_version, :max_version)
219
+
220
+ def verify?
221
+ verify != false
222
+ end
223
+
224
+ def disable?
225
+ !verify?
226
+ end
227
+ end
228
+
229
+ class ProxyOptions < Options.new(:uri, :user, :password)
230
+ extend Forwardable
231
+ def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path=
232
+
233
+ def self.from(value)
234
+ case value
235
+ when String
236
+ value = {:uri => Utils.URI(value)}
237
+ when URI
238
+ value = {:uri => value}
239
+ when Hash, Options
240
+ if uri = value.delete(:uri)
241
+ value[:uri] = Utils.URI(uri)
242
+ end
243
+ end
244
+ super(value)
245
+ end
246
+
247
+ memoized(:user) { uri && uri.user && Utils.unescape(uri.user) }
248
+ memoized(:password) { uri && uri.password && Utils.unescape(uri.password) }
249
+ end
250
+
251
+ class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
252
+ :parallel_manager, :params, :headers, :builder_class)
253
+
254
+ options :request => RequestOptions, :ssl => SSLOptions
255
+
256
+ memoized(:request) { self.class.options_for(:request).new }
257
+
258
+ memoized(:ssl) { self.class.options_for(:ssl).new }
259
+
260
+ memoized(:builder_class) { RackBuilder }
261
+
262
+ def new_builder(block)
263
+ builder_class.new(&block)
264
+ end
265
+ end
266
+
267
+ class Env < Options.new(:method, :body, :url, :request, :request_headers,
268
+ :ssl, :parallel_manager, :params, :response, :response_headers, :status,
269
+ :reason_phrase)
270
+
271
+ ContentLength = 'Content-Length'.freeze
272
+ StatusesWithoutBody = Set.new [204, 304]
273
+ SuccessfulStatuses = 200..299
274
+
275
+ # A Set of HTTP verbs that typically send a body. If no body is set for
276
+ # these requests, the Content-Length header is set to 0.
277
+ MethodsWithBodies = Set.new [:post, :put, :patch, :options]
278
+
279
+ options :request => RequestOptions,
280
+ :request_headers => Utils::Headers, :response_headers => Utils::Headers
281
+
282
+ extend Forwardable
283
+
284
+ def_delegators :request, :params_encoder
285
+
286
+ # Public
287
+ def self.from(value)
288
+ env = super(value)
289
+ if value.respond_to?(:custom_members)
290
+ env.custom_members.update(value.custom_members)
291
+ end
292
+ env
293
+ end
294
+
295
+ # Public
296
+ def [](key)
297
+ if in_member_set?(key)
298
+ super(key)
299
+ else
300
+ custom_members[key]
301
+ end
302
+ end
303
+
304
+ # Public
305
+ def []=(key, value)
306
+ if in_member_set?(key)
307
+ super(key, value)
308
+ else
309
+ custom_members[key] = value
310
+ end
311
+ end
312
+
313
+ # Public
314
+ def success?
315
+ SuccessfulStatuses.include?(status)
316
+ end
317
+
318
+ # Public
319
+ def needs_body?
320
+ !body && MethodsWithBodies.include?(method)
321
+ end
322
+
323
+ # Public
324
+ def clear_body
325
+ request_headers[ContentLength] = '0'
326
+ self.body = ''
327
+ end
328
+
329
+ # Public
330
+ def parse_body?
331
+ !StatusesWithoutBody.include?(status)
332
+ end
333
+
334
+ # Public
335
+ def parallel?
336
+ !!parallel_manager
337
+ end
338
+
339
+ def inspect
340
+ attrs = [nil]
341
+ members.each do |mem|
342
+ if value = send(mem)
343
+ attrs << "@#{mem}=#{value.inspect}"
344
+ end
345
+ end
346
+ if !custom_members.empty?
347
+ attrs << "@custom=#{custom_members.inspect}"
348
+ end
349
+ %(#<#{self.class}#{attrs.join(" ")}>)
350
+ end
351
+
352
+ # Internal
353
+ def custom_members
354
+ @custom_members ||= {}
355
+ end
356
+
357
+ # Internal
358
+ if members.first.is_a?(Symbol)
359
+ def in_member_set?(key)
360
+ self.class.member_set.include?(key.to_sym)
361
+ end
362
+ else
363
+ def in_member_set?(key)
364
+ self.class.member_set.include?(key.to_s)
365
+ end
366
+ end
367
+
368
+ # Internal
369
+ def self.member_set
370
+ @member_set ||= Set.new(members)
371
+ end
372
+ end
373
+ end