attestor 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/README.md +69 -55
  4. data/config/metrics/rubocop.yml +4 -0
  5. data/lib/attestor/invalid_error.rb +1 -12
  6. data/lib/attestor/policy/and.rb +2 -19
  7. data/lib/attestor/policy/negator.rb +2 -29
  8. data/lib/attestor/policy/node.rb +7 -33
  9. data/lib/attestor/policy/not.rb +2 -27
  10. data/lib/attestor/policy/or.rb +2 -19
  11. data/lib/attestor/policy/xor.rb +2 -19
  12. data/lib/attestor/policy.rb +0 -18
  13. data/lib/attestor/report.rb +51 -0
  14. data/lib/attestor/validations/delegator.rb +3 -18
  15. data/lib/attestor/validations/message.rb +1 -16
  16. data/lib/attestor/validations/reporter.rb +21 -0
  17. data/lib/attestor/validations/validator.rb +4 -63
  18. data/lib/attestor/validations/validators.rb +13 -41
  19. data/lib/attestor/validations.rb +12 -1
  20. data/lib/attestor/version.rb +1 -1
  21. data/lib/attestor.rb +2 -0
  22. data/spec/features/example_spec.rb +5 -5
  23. data/spec/support/policies.rb +5 -5
  24. data/spec/tests/policy/and_spec.rb +2 -2
  25. data/spec/tests/policy/node_spec.rb +9 -9
  26. data/spec/tests/policy/not_spec.rb +2 -2
  27. data/spec/tests/policy/or_spec.rb +2 -2
  28. data/spec/tests/policy/xor_spec.rb +2 -2
  29. data/spec/tests/policy_spec.rb +0 -48
  30. data/spec/tests/report_spec.rb +106 -0
  31. data/spec/tests/validations/delegator_spec.rb +6 -6
  32. data/spec/tests/validations/message_spec.rb +1 -1
  33. data/spec/tests/validations/reporter_spec.rb +47 -0
  34. data/spec/tests/validations/validator_spec.rb +56 -74
  35. data/spec/tests/validations/validators_spec.rb +81 -4
  36. data/spec/tests/validations_spec.rb +98 -10
  37. metadata +9 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3030abf371f99cd2d342e227e8d054fb718d0597
4
- data.tar.gz: 9742b46202b9d6e2cff850f6e52243f8668bdde7
3
+ metadata.gz: 1b4d9f6dfa1931f69062623223fafaf32ec4d158
4
+ data.tar.gz: a18709a18446af9c58a71287eb9b3d923c125aed
5
5
  SHA512:
6
- metadata.gz: 83ae6c32bb6be7ba47304a37ddc811ac3a22f761883dfc432f79533a2b7904860c2c81bd32e36b11d7e46e6ee445943074391dd90dab306e24548d2da58fd8d6
7
- data.tar.gz: a1d2d13d9c1c7b16e942755b482dae67d8aefb1ed12ee4ca31755d4b31d787ed5db915190af62ba4ed7893b98a6ceb41d6feacd4c7b2c7e8e3ff925291528d5b
6
+ metadata.gz: fadf072439c8efa450e86ff37f7dd5778c67ce7a087eaea4e012e0267d9dce2b877283b76c04b1434b1ab0d17b7a00a48317ddeee766304d7ff08ebb02c66109
7
+ data.tar.gz: 0c5fccf30fe0cfc574d71b3235da77acc904c3f537f4d9ffb4887184925d42c57d82736f99d0af0954fe000c0bc141cc1793bd15911275485bfcfc648bb235c6
data/.travis.yml CHANGED
@@ -6,5 +6,4 @@ rvm:
6
6
  - '2.0'
7
7
  - ruby-head
8
8
  - rbx-2 --2.0
9
- - jruby-head-20mode
10
- - jruby-head-21mode
9
+ - jruby-9.0.0.0.pre1
data/README.md CHANGED
@@ -37,7 +37,12 @@ To solve the problem, the `attestor` gem:
37
37
  Approach
38
38
  --------
39
39
 
40
- Instead of collecting errors inside the object, the module's `validate` instance method just raises an exception (`Attestor::InvalidError`), that carries errors outside of the object. The object stays untouched (and can be made immutable).
40
+ Instead of collecting errors inside the object, the module defines two instance methods:
41
+
42
+ * `validate!` raises an exception (`Attestor::InvalidError`), that carries errors outside of the object.
43
+ * `validate` - the safe version of `validate!`. It rescues an exception and returns special result object, that carries error info outside of the object.
44
+
45
+ The object stays untouched (and can be made immutable).
41
46
 
42
47
  Installation
43
48
  ------------
@@ -67,18 +72,10 @@ Basic Use
67
72
  Declare validation in the same way as ActiveModel's `.validate` method does:
68
73
 
69
74
  ```ruby
70
- class Transfer < Struct.new(:debet, :credit)
75
+ Transfer = Struct.new(:debet, :credit) do
71
76
  include Attestor::Validations
72
77
 
73
78
  validate :consistent
74
- end
75
- ```
76
-
77
- You have to define an instance validator method (that can be private):
78
-
79
- ```ruby
80
- class Transfer < Struct.new(:debet, :credit)
81
- # ...
82
79
 
83
80
  private
84
81
 
@@ -89,7 +86,16 @@ class Transfer < Struct.new(:debet, :credit)
89
86
  end
90
87
  ```
91
88
 
92
- The `#invalid` method translates its argument in a current class scope and raises an exception.
89
+ Alternatively, you can describe validation in the block (called in the scope of instance):
90
+
91
+ ```ruby
92
+ class Transfer
93
+ # ...
94
+ validate { invalid :inconsistent if credit.sum != debet.sum }
95
+ end
96
+ ```
97
+
98
+ The `#invalid` method translates its argument in a current class scope and raises an exception with that method.
93
99
 
94
100
  ```yaml
95
101
  # config/locales/en.yml
@@ -101,16 +107,7 @@ en:
101
107
  inconsistent: "Credit differs from debet by %{fraud}"
102
108
  ```
103
109
 
104
- Alternatively, you can describe validation in the block (called in the scope of instance):
105
-
106
- ```ruby
107
- class Transfer
108
- # ...
109
- validate { invalid :inconsistent if credit.sum != debet.sum }
110
- end
111
- ```
112
-
113
- To run validations use the `#validate` instance method:
110
+ To run validations use the `#validate!` instance method:
114
111
 
115
112
  ```ruby
116
113
  debet = OpenStruct.new(sum: 100)
@@ -118,14 +115,25 @@ credit = OpenStruct.new(sum: 90)
118
115
  fraud_transfer = Transfer.new(debet, credit)
119
116
 
120
117
  begin
121
- transfer.validate
122
- rescue => error
118
+ transfer.validate! # with the bang
119
+ rescue Attestor::InvalidError => error
123
120
  error.object == transfer # => true
124
- error.messages
125
- # => ["Credit differs from debet by 10"]
121
+ error.messages # => ["Credit differs from debet by 10"]
126
122
  end
127
123
  ```
128
124
 
125
+ Another option is to use the safe version `#validate`. It rescues from the exception and returns results in a separate report object:
126
+
127
+ ```ruby
128
+ report = transfer.validate # without the bang
129
+
130
+ report.valid? # => false
131
+ report.invalid? # => true
132
+ report.object == transfer # => true
133
+ report.messages # => ["Credit differs from debet by 10"]
134
+ report.error # => <Attestor::InvalidError ...>
135
+ ```
136
+
129
137
  Use of Contexts
130
138
  ---------------
131
139
 
@@ -134,43 +142,42 @@ Sometimes you need to validate the object agaist the subset of validations, not
134
142
  To do this use `:except` and `:only` options of the `.validate` class method.
135
143
 
136
144
  ```ruby
137
- class Transfer < Struct.new(:debet, :credit)
138
- include Attestor::Validations
139
-
145
+ class Transfer
146
+ # ...
140
147
  validate :consistent, except: :steal_of_money
141
148
  end
142
149
  ```
143
150
 
144
- Then call a validate method with that context:
151
+ Then call a `#validate!`/`#validate` methods with that context:
145
152
 
146
153
  ```ruby
147
- fraud_transfer.validate # => InvalidError
148
- fraud_transfer.validate :steal_of_money # => PASSES!
154
+ fraud_transfer.validate! # => InvalidError
155
+ fraud_transfer.validate! :steal_of_money # => PASSES!
149
156
  ```
150
157
 
151
- You can use the same validator several times with different contexts. Any validation will be made independently from the others:
158
+ You can use the same validator several times with different contexts.
152
159
 
153
160
  ```ruby
154
161
  class Transfer
155
162
  # ...
163
+
164
+ # validate :consistent, only: [:fair_trade, :legal]
156
165
  validate :consistent, only: :fair_trade
157
166
  validate :consistent, only: :legal
158
167
 
159
- # This is the same as:
160
- # validate :consistent, only: [:fair_trade, :legal]
161
168
  end
162
169
  ```
163
170
 
164
171
  Delegation
165
172
  ----------
166
173
 
167
- Extract validator to the external object (policy), that responds to `validate`.
174
+ Extract validator to an external object (policy), that responds to `validate!`.
168
175
 
169
176
  ```ruby
170
- class ConsistentTransfer.new(:debet, :credit)
177
+ ConsistentTransfer = Struct.new(:debet, :credit) do
171
178
  include Attestor::Validations
172
179
 
173
- def validate
180
+ def validate!
174
181
  invalid :inconsistent unless debet.sum == credit.sum
175
182
  end
176
183
  end
@@ -197,36 +204,36 @@ class Transfer
197
204
  end
198
205
  ```
199
206
 
200
- The change between `validate :something` and `validates :something` is that:
201
- * `validate` expects `#something` to make checks and raise error by itself
202
- * `validates` expects `#something` to respond to `#validate`
207
+ The difference between `.validate :something` and `.validates :something` class methods is that:
208
+ * `.validate` expects `#something` to make checks and raise error by itself
209
+ * `.validates` expects `#something` to respond to `#validate!`
203
210
 
204
211
  Policy Objects
205
212
  --------------
206
213
 
207
- Basically the policy includes `Attestor::Validations` with additional methods to allow chaining policies by logical methods.
214
+ Basically the policy includes `Attestor::Validations` with additional methods to allow logical compositions.
208
215
 
209
- To create a policy as a `Struct` use the builder method:
216
+ To create a policy as a `Struct` use the builder:
210
217
 
211
218
  ```ruby
212
219
  ConsistencyPolicy = Attestor::Policy.new(:debet, :credit) do
213
- def validate
220
+ def validate!
214
221
  fraud = credit - debet
215
222
  invalid :inconsistent, fraud: fraud if fraud != 0
216
223
  end
217
224
  end
218
225
  ```
219
226
 
220
- If you doesn't need Struct, include `Attestor::Policy` to the class and initialize its arguments somehow else:
227
+ If you doesn't need `Struct`, include `Attestor::Policy` to the class and initialize its arguments somehow else:
221
228
 
222
229
  ```ruby
223
- class ConsistencyPolicy < OpenStruct
230
+ class ConsistencyPolicy
224
231
  include Attestor::Policy
225
232
  # ...
226
233
  end
227
234
  ```
228
235
 
229
- Policy objects can be used by `validates` method like other validatable objects:
236
+ Policy objects can be used by `validates` method like other objects that respond to `#validate!`:
230
237
 
231
238
  ```ruby
232
239
  class Transfer
@@ -235,8 +242,6 @@ class Transfer
235
242
  end
236
243
  ```
237
244
 
238
- They also respond to `valid?` and `invalid?` methods (that just rescues from `vaidate` missing any error messages).
239
-
240
245
  Complex Policies
241
246
  ----------------
242
247
 
@@ -245,27 +250,27 @@ Policies (assertions) can be combined by logical methods.
245
250
  Suppose we have two policy objects:
246
251
 
247
252
  ```ruby
248
- valid_policy.valid? # => true
249
- invalid_policy.valid? # => false
253
+ valid_policy.validate.valid? # => true
254
+ invalid_policy.validate.valid? # => false
250
255
  ```
251
256
 
252
257
  Use factory methods to provide compositions:
253
258
 
254
259
  ```ruby
255
260
  complex_policy = valid_policy.not
256
- complex_policy.validate # => fails
261
+ complex_policy.validate! # => fails
257
262
 
258
263
  complex_policy = valid_policy.and(valid_policy, invalid_policy)
259
- complex_policy.validate # => fails
264
+ complex_policy.validate! # => fails
260
265
 
261
266
  complex_policy = invalid_policy.or(invalid_policy, valid_policy)
262
- complex_policy.validate # => passes
267
+ complex_policy.validate! # => passes
263
268
 
264
269
  complex_policy = valid_policy.xor(valid_poicy, valid_policy)
265
- complex_policy.validate # => fails
270
+ complex_policy.validate! # => fails
266
271
 
267
272
  complex_policy = valid_policy.xor(valid_poicy, invalid_policy)
268
- complex_policy.validate # => passes
273
+ complex_policy.validate! # => passes
269
274
  ```
270
275
 
271
276
  The `or`, `and` and `xor` methods called without argument(s) don't provide a policy object. They return lazy composer, expecting `#not` method.
@@ -294,6 +299,15 @@ Policy.not(valid_policy)
294
299
 
295
300
  As before, you can use any number of policies (except for negation of a single policy) at any number of nesting.
296
301
 
302
+ Latest changes
303
+ --------------
304
+
305
+ ### Version 2.0.0
306
+
307
+ 1. The method `#validate` doesn't raise an error. It returns a validation results report. To raise an exception use the unsafe `#validate!` method (see [Basic Use](#basic-use) for details).
308
+
309
+ 2. Policies doesn't have `#valid?` and `#invalid?` methods any longer. Both the methods are removed to `#validate` report.
310
+
297
311
  Compatibility
298
312
  -------------
299
313
 
@@ -52,6 +52,10 @@ Style/FileName:
52
52
  Style/RaiseArgs:
53
53
  EnforcedStyle: compact
54
54
 
55
+ Style/RescueModifier:
56
+ Exclude:
57
+ - '**/*_spec.rb'
58
+
55
59
  Style/SingleLineMethods:
56
60
  Exclude:
57
61
  - '**/*_spec.rb'
@@ -2,20 +2,9 @@
2
2
 
3
3
  module Attestor
4
4
 
5
- # The exception to be raised when a validation fails
5
+ # The exception to be raised when an unsafe validation fails
6
6
  class InvalidError < RuntimeError
7
7
 
8
- # @!scope class
9
- # @!method new(object, messages = nil)
10
- # Creates an exception for given object
11
- #
12
- # @param [Object] object
13
- # The invalid object
14
- # @param [String, Array<String>] messages
15
- # The list of validation error messages
16
- #
17
- # @return [Attestor::InvalidError]
18
-
19
8
  # @private
20
9
  def initialize(object, messages = nil)
21
10
  @object = object
@@ -4,27 +4,10 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # AND-concatenation of several policies (branches)
8
- #
9
- # The policy is valid if all its branches are valid.
10
- #
11
- # @example (see #validate)
12
- #
13
- # @api private
7
+ # @private
14
8
  class And < Node
15
9
 
16
- # Checks whether every policy is valid
17
- #
18
- # @example
19
- # first.valid? # => true
20
- # second.valid? # => false
21
- #
22
- # composition = Attestor::Policy::And.new(first, second)
23
- # composition.validate
24
- # # => Policy::InvalidError
25
- #
26
- # @return [undefined]
27
- def validate
10
+ def validate!
28
11
  return unless detect(&:invalid?)
29
12
  super
30
13
  end
@@ -4,47 +4,20 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # Composes a policy with an argument of its {#not} method
8
- #
9
- # @api private
7
+ # @private
10
8
  class Negator
11
9
 
12
- # @!scope class
13
- # @!method new(policy, composer)
14
- # Creates the negator object, expecting {#not} method call
15
- #
16
- # @param [Policy::Base] policy
17
- # the policy to be composed with negations of other policies
18
- # @param [Class] composer
19
- # the composer for policies
20
- #
21
- # @return [Policy::Base::Negator]
22
10
  def initialize(composer, policy)
23
11
  @policy = policy
24
12
  @composer = composer
25
13
  freeze
26
14
  end
27
15
 
28
- # Returns a composition of the {#policy} with negations of other policies
29
- #
30
- # @param [Policy::Base, Array<Policy::Base>] policies
31
- #
32
- # @return [Policy::Base]
33
16
  def not(*policies)
34
17
  composer.new policy, policies.flat_map(&Not.method(:new))
35
18
  end
36
19
 
37
- # @!attribute [r] policy
38
- # The the policy to be composed with negations of other policies
39
- #
40
- # @return [Policy::Base]
41
- attr_reader :policy
42
-
43
- # @!attribute [r] composer
44
- # The the composer for policies
45
- #
46
- # @return [Class]
47
- attr_reader :composer
20
+ attr_reader :policy, :composer
48
21
 
49
22
  end # class Negator
50
23
 
@@ -4,49 +4,23 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # The base class for composite policies
8
- #
9
- # @api private
7
+ # @private
10
8
  class Node
11
- include Attestor::Policy
12
- include Enumerable
13
-
14
- # @!scope class
15
- # @!method new(*branches)
16
- # Creates the node with branches
17
- #
18
- # @param [<Attestor::Policy>, Array<Attestor::Policy>] branches
19
- #
20
- # @return [Policy::Base::Node]
21
-
22
- # @private
9
+ include Attestor::Policy, Enumerable
10
+
11
+ attr_reader :branches
12
+
23
13
  def initialize(*branches)
24
14
  @branches = branches.flatten
25
15
  freeze
26
16
  end
27
17
 
28
- # @!attribute [r] branches
29
- # The branches of the node
30
- #
31
- # @return [Array<Policy::Base>]
32
- attr_reader :branches
33
-
34
- # Validates the policy as invalid
35
- #
36
- # To be reloaded by subclasses (And, Or, Xor, Not)
37
- #
38
- # @raise [Policy::InvalidError]
39
- #
40
- # @return [undefined]
41
- def validate
18
+ def validate!
42
19
  invalid :base
43
20
  end
44
21
 
45
- # Iterates throught branches
46
- #
47
- # @return [Enumerator]
48
22
  def each
49
- block_given? ? branches.each { |item| yield(item) } : to_enum
23
+ block_given? ? branches.each { |item| yield(item.validate) } : to_enum
50
24
  end
51
25
 
52
26
  end # class Node
@@ -4,39 +4,14 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # Negation of a single policy
8
- #
9
- # The policy is valid if its only branch is invalid
10
- #
11
- # @example (see #validate)
12
- #
13
- # @api private
7
+ # @private
14
8
  class Not < Node
15
9
 
16
- # @!scope class
17
- # @!method new(policy)
18
- # Creates the policy negation
19
- #
20
- # @param [Array<Policy::Base>] policy
21
- #
22
- # @return [Policy::Base::Node]
23
-
24
- # @private
25
10
  def initialize(_)
26
11
  super
27
12
  end
28
13
 
29
- # Checks whether every policy is valid
30
- #
31
- # @example
32
- # policy.valid? # => true
33
- #
34
- # composition = Attestor::Policy::Not.new(policy)
35
- # composition.validate
36
- # # => Policy::InvalidError
37
- #
38
- # @return [undefined]
39
- def validate
14
+ def validate!
40
15
  return unless detect(&:valid?)
41
16
  super
42
17
  end
@@ -4,27 +4,10 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # OR-concatenation of several policies (branches)
8
- #
9
- # The policy is valid unless all its branches are invalid.
10
- #
11
- # @example (see #validate)
12
- #
13
- # @api private
7
+ # @private
14
8
  class Or < Node
15
9
 
16
- # Checks whether any policy is valid
17
- #
18
- # @example
19
- # first.valid? # => false
20
- # second.valid? # => false
21
- #
22
- # composition = Attestor::Policy::Or.new(first, second)
23
- # composition.validate
24
- # # => Policy::InvalidError
25
- #
26
- # @return [undefined]
27
- def validate
10
+ def validate!
28
11
  return if detect(&:valid?)
29
12
  super
30
13
  end
@@ -4,27 +4,10 @@ module Attestor
4
4
 
5
5
  module Policy
6
6
 
7
- # XOR-concatenation of several policies (branches)
8
- #
9
- # The policy is valid when it has both valid and invalid branches.
10
- #
11
- # @example (see #validate)
12
- #
13
- # @api private
7
+ # @private
14
8
  class Xor < Node
15
9
 
16
- # Checks whether both valid and invalid branches are present
17
- #
18
- # @example
19
- # first.valid? # => true
20
- # second.valid? # => true
21
- #
22
- # composition = Attestor::Policy::Xor.new(first, second)
23
- # composition.validate
24
- # # => Policy::InvalidError
25
- #
26
- # @return [undefined]
27
- def validate
10
+ def validate!
28
11
  return if detect(&:valid?) && detect(&:invalid?)
29
12
  super
30
13
  end
@@ -36,24 +36,6 @@ module Attestor
36
36
  end
37
37
  end
38
38
 
39
- # Checks whether the policy is valid
40
- #
41
- # @return [Boolean]
42
- def valid?
43
- validate
44
- rescue InvalidError
45
- false
46
- else
47
- true
48
- end
49
-
50
- # Checks whether the policy is invalid
51
- #
52
- # @return [Boolean]
53
- def invalid?
54
- !valid?
55
- end
56
-
57
39
  # Negates the current policy
58
40
  #
59
41
  # @return [Attestor::Policy::Not]
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ module Attestor
4
+
5
+ # Describes the result, returned by safe validation
6
+ class Report
7
+
8
+ # @private
9
+ def initialize(object, error = nil)
10
+ @object = object
11
+ @error = error
12
+ freeze
13
+ end
14
+
15
+ # @!attribute [r] object
16
+ # The object being validated
17
+ #
18
+ # @return [Object]
19
+ attr_reader :object
20
+
21
+ # @!attribute [r] error
22
+ # The exception raised by validation
23
+ #
24
+ # @return [Attestor::InvalidError] if validation fails
25
+ # @return [nil] if validation passes
26
+ attr_reader :error
27
+
28
+ # Checks whether validation passes
29
+ #
30
+ # @return [Boolean]
31
+ def valid?
32
+ error.blank?
33
+ end
34
+
35
+ # Checks whether validation fails
36
+ #
37
+ # @return [Boolean]
38
+ def invalid?
39
+ !valid?
40
+ end
41
+
42
+ # Returns the list of error messages
43
+ #
44
+ # @return [Array<String>]
45
+ def messages
46
+ error ? error.messages : []
47
+ end
48
+
49
+ end # class Report
50
+
51
+ end # module Attestor
@@ -4,26 +4,11 @@ module Attestor
4
4
 
5
5
  module Validations
6
6
 
7
- # Describe a validator that delegates validation to instance method or block
8
- #
9
- # The follower not only calls an instance method (block) as validator does,
10
- # but calls #validate method of the result.
11
- #
12
- # @example
13
- # follower = Validator.new(:foo, only: :baz) { FooPolicy.new(foo) }
14
- #
15
- # @api private
7
+ # @private
16
8
  class Delegator < Validator
17
9
 
18
- # Validates an object by delegation
19
- #
20
- # @param [Object] object
21
- #
22
- # @raise [Attestor::InvalidError] if a policy isn't valid
23
- #
24
- # @return [undefined]
25
- def validate(_)
26
- super.validate
10
+ def validate!(_)
11
+ super.validate!
27
12
  end
28
13
 
29
14
  end # class Follower
@@ -4,24 +4,9 @@ module Attestor
4
4
 
5
5
  module Validations
6
6
 
7
- # Bulder for error messages
8
- #
9
- # @api private
7
+ # @private
10
8
  class Message < String
11
9
 
12
- # @!scope class
13
- # @!method new(value, object, options = {})
14
- # Builds a string from value
15
- #
16
- # @param [#to_s] value
17
- # @param [Object] object
18
- # @param [Hash] options
19
- # options for translating symbolic value
20
- #
21
- # @return [String]
22
- # either translation of symbolic value or stringified value argument
23
-
24
- # @private
25
10
  def initialize(value, object, options = {})
26
11
  @value = value
27
12
  @object = object