clowne 1.3.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -1
  3. data/README.md +1 -4
  4. data/lib/clowne/adapters/active_record/associations/belongs_to.rb +0 -1
  5. data/lib/clowne/adapters/active_record/associations/has_one.rb +0 -1
  6. data/lib/clowne/adapters/active_record/associations/noop.rb +1 -1
  7. data/lib/clowne/adapters/active_record/associations.rb +1 -1
  8. data/lib/clowne/adapters/active_record/resolvers/association.rb +0 -1
  9. data/lib/clowne/adapters/base/association.rb +1 -1
  10. data/lib/clowne/adapters/base.rb +2 -2
  11. data/lib/clowne/adapters/sequel/associations/one_to_many.rb +0 -4
  12. data/lib/clowne/adapters/sequel/associations.rb +1 -1
  13. data/lib/clowne/adapters/sequel/resolvers/association.rb +0 -1
  14. data/lib/clowne/cloner.rb +1 -1
  15. data/lib/clowne/declarations/trait.rb +1 -1
  16. data/lib/clowne/declarations.rb +1 -1
  17. data/lib/clowne/ext/record_key.rb +1 -1
  18. data/lib/clowne/resolvers/after_clone.rb +2 -1
  19. data/lib/clowne/resolvers/init_as.rb +0 -1
  20. data/lib/clowne/rspec/clone_association.rb +0 -1
  21. data/lib/clowne/version.rb +1 -1
  22. data/lib/clowne.rb +2 -3
  23. metadata +7 -110
  24. data/.codeclimate.yml +0 -7
  25. data/.gitattributes +0 -1
  26. data/.gitignore +0 -16
  27. data/.rspec +0 -3
  28. data/.rubocop.yml +0 -28
  29. data/.rufo +0 -3
  30. data/.travis.yml +0 -45
  31. data/Gemfile +0 -20
  32. data/Rakefile +0 -8
  33. data/bin/console +0 -14
  34. data/bin/setup +0 -8
  35. data/clowne.gemspec +0 -36
  36. data/docs/.nojekyll +0 -0
  37. data/docs/.rubocop.yml +0 -18
  38. data/docs/CNAME +0 -1
  39. data/docs/README.md +0 -131
  40. data/docs/_sidebar.md +0 -25
  41. data/docs/active_record.md +0 -33
  42. data/docs/after_clone.md +0 -53
  43. data/docs/after_persist.md +0 -77
  44. data/docs/architecture.md +0 -138
  45. data/docs/assets/docsify.min.js +0 -1
  46. data/docs/assets/prism-ruby.min.js +0 -1
  47. data/docs/assets/styles.css +0 -348
  48. data/docs/assets/vue.css +0 -1
  49. data/docs/clone_mapper.md +0 -59
  50. data/docs/customization.md +0 -63
  51. data/docs/exclude_association.md +0 -61
  52. data/docs/finalize.md +0 -31
  53. data/docs/from_v02_to_v1.md +0 -83
  54. data/docs/getting_started.md +0 -171
  55. data/docs/implicit_cloner.md +0 -33
  56. data/docs/include_association.md +0 -133
  57. data/docs/index.html +0 -29
  58. data/docs/init_as.md +0 -40
  59. data/docs/inline_configuration.md +0 -37
  60. data/docs/nullify.md +0 -33
  61. data/docs/operation.md +0 -55
  62. data/docs/parameters.md +0 -112
  63. data/docs/sequel.md +0 -50
  64. data/docs/supported_adapters.md +0 -10
  65. data/docs/testing.md +0 -194
  66. data/docs/traits.md +0 -25
  67. data/gemfiles/activerecord42.gemfile +0 -9
  68. data/gemfiles/jruby.gemfile +0 -10
  69. data/gemfiles/railsmaster.gemfile +0 -10
  70. data/lib/clowne/ext/yield_self_then.rb +0 -25
@@ -1,63 +0,0 @@
1
- # Customization
2
-
3
- Clowne is built with extensibility in mind. You can create your own DSL commands and resolvers.
4
-
5
- Let's consider an example.
6
-
7
- Suppose that you want to add the `include_all` declaration to automagically include all associations (for ActiveRecord).
8
-
9
- First, you should add a custom declaration:
10
-
11
- ```ruby
12
- # Extend from Base declaration
13
- class IncludeAll < Clowne::Declarations::Base # :nodoc: all
14
- def compile(plan)
15
- # Just add all_associations declaration (self) to plan
16
- plan.set(:all_associations, self)
17
- end
18
- end
19
-
20
- # Register our declrations, i.e. extend DSL
21
- Clowne::Declarations.add :include_all, IncludeAll
22
- ```
23
-
24
- See more on `plan` in [architecture overview](architecture.md).
25
-
26
- Secondly, register a resolver:
27
-
28
- ```ruby
29
- class AllAssociations
30
- # This method is called when all_associations command is applied.
31
- #
32
- # source – source record
33
- # record – target record (our clone)
34
- # declaration – declaration object
35
- # params – custom params passed to cloner
36
- def call(source, record, declaration, params:)
37
- source.class.reflections.each_value do |_name, reflection|
38
- # Exclude belongs_to associations
39
- next if reflection.macro == :belongs_to
40
-
41
- # Resolve and apply association cloner
42
- cloner_class = Clowne::Adapters::ActiveRecord::Associations.cloner_for(reflection)
43
- cloner_class.new(reflection, source, declaration, params).call(record)
44
- end
45
- record
46
- end
47
- end
48
-
49
- # Finally, register the resolver
50
- Clowne::Adapters::ActiveRecord.register_resolver(
51
- :all_associations, AllAssociations
52
- )
53
- ```
54
-
55
- Now you can use it likes this:
56
-
57
- ```ruby
58
- class UserCloner < Clowne::Cloner
59
- adapter :active_record
60
-
61
- include_all
62
- end
63
- ```
@@ -1,61 +0,0 @@
1
- # Exclude Association
2
-
3
- Clowne doesn't include any association by default and doesn't provide _magic_ `include_all` declaration (although you can [add one by yourself](customization.md)).
4
-
5
- Nevertheless, sometimes you might want to exclude already added associations (when inheriting a cloner or using [traits](traits.md)).
6
-
7
- Consider an example:
8
-
9
- ```ruby
10
- class UserCloner < Clowne::Cloner
11
- include_association :posts
12
-
13
- trait :without_posts do
14
- exclude_association :posts
15
- end
16
- end
17
-
18
- # copy user and posts
19
- clone = UserCloner.call(user).to_record
20
- clone.posts.count == user.posts.count
21
- # => true
22
-
23
- # copy only user
24
- clone2 = UserCloner.call(user, traits: :without_posts).to_record
25
- clone2.posts
26
- # => []
27
- ```
28
-
29
- **NOTE**: once excluded association cannot be re-included, e.g. the following cloner:
30
-
31
- ```ruby
32
- class UserCloner < Clowne::Cloner
33
- exclude_association :comments
34
-
35
- trait :with_comments do
36
- # That wouldn't work
37
- include_association :comments
38
- end
39
- end
40
-
41
- clone = UserCloner.call(user, traits: :with_comments).to_record
42
- clone.comments.empty?
43
- # => true
44
- ```
45
-
46
- Why so? That allows us to have a deterministic cloning plan when combining multiple traits
47
- (or inheriting cloners).
48
-
49
- ## Exclude multiple associations
50
-
51
- It's possible to exclude multiple associations at once the same way as [`include_associations`](include_association.md):
52
-
53
- ```ruby
54
- class UserCloner < Clowne::Cloner
55
- include_associations :accounts, :posts, :comments
56
-
57
- trait :without_posts do
58
- exclude_associations :posts, :comments
59
- end
60
- end
61
- ```
data/docs/finalize.md DELETED
@@ -1,31 +0,0 @@
1
- # Finalization
2
-
3
- To apply custom transformations to the cloned record, you can use the `finalize` declaration:
4
-
5
- ```ruby
6
- class UserCloner < Clowne::Cloner
7
- finalize do |_source, record, **_params|
8
- record.name = "This is copy!"
9
- end
10
-
11
- trait :change_email do
12
- finalize do |_source, record, **params|
13
- record.email = params[:email]
14
- end
15
- end
16
- end
17
-
18
- cloned = UserCloner.call(user).to_record
19
- cloned.name
20
- # => 'This is copy!'
21
- cloned.email == "clone@example.com"
22
- # => false
23
-
24
- cloned2 = UserCloner.call(user, traits: :change_email).to_record
25
- cloned2.name
26
- # => 'This is copy!'
27
- cloned2.email
28
- # => 'clone@example.com'
29
- ```
30
-
31
- Finalization blocks are called at the end of the [cloning process](getting_started?id=execution-order).
@@ -1,83 +0,0 @@
1
- # From v0.2.x to v1.0.0
2
-
3
- The breaking change of v1.0 is the return of a unified [`result object`](operation.md) for all adapters.
4
-
5
- ## ActiveRecord
6
-
7
- ### Update code to work with [`Operation`](operation.md)
8
-
9
- ```ruby
10
- # Before
11
- clone = UserCloner.call(user)
12
- # => <#User id: nil, ...>
13
- clone.save!
14
- # => true
15
-
16
- # After
17
- clone = UserCloner.call(user)
18
- # => <#Clowne::Utils::Operation ...>
19
- clone = clone.to_record
20
- # => <#User id: 2, ...>
21
- clone.save!
22
- # => true
23
-
24
- # After (even better because of using full functionality)
25
- operation = UserCloner.call(user)
26
- # => <#Clowne::Utils::Operation ...>
27
- operation.persist!
28
- # => true
29
- clone = operation.to_record
30
- # => <#User id: 2, ...>
31
- clone.persisted?
32
- # => true
33
- ```
34
-
35
- ### Move post-processing cloning logic into [`after_persist`](after_persist.md) callback (if you have it)
36
-
37
- *Notice: `after_persist` supported only with [`active_record`](active_record.md) adapter.*
38
-
39
- ```ruby
40
- # Before
41
- clone = UserCloner.call(user)
42
- clone.save!
43
- # do something with persisted clone
44
-
45
- # After
46
- class UserCloner < Clowne::Cloner
47
- # ...
48
- after_persist do |origin, clone, **|
49
- # do something with persisted clone
50
- end
51
- end
52
-
53
- clone = UserCloner.call(user).tap(&:persist).to_record
54
- ```
55
- ## Sequel
56
-
57
- ### Use `to_record` instead of `to_model`
58
-
59
- ```ruby
60
- # Before
61
- record_wrapper = UserCloner.call(user)
62
- clone = record_wrapper.to_model
63
- clone.new?
64
- # => true
65
-
66
- # After
67
- operation = UserCloner.call(user)
68
- clone = operation.to_record
69
- clone.new?
70
- # => true
71
- ```
72
-
73
- ### Use `operation#persist` instead of converting to model and calling `#save`
74
-
75
- ```ruby
76
- # Before
77
- record_wrapper = UserCloner.call(user)
78
- clone = record_wrapper.to_model
79
- clone.save
80
-
81
- # After
82
- clone = UserCloner.call(user).tap(&:persist).to_record
83
- ```
@@ -1,171 +0,0 @@
1
- # Getting Started
2
-
3
- ## Installation
4
-
5
- To install Clowne with RubyGems:
6
-
7
- ```ruby
8
- gem install clowne
9
- ```
10
-
11
- Or add this line to your application's Gemfile:
12
-
13
- ```ruby
14
- gem "clowne"
15
- ```
16
-
17
- ## Configuration
18
-
19
- Basic cloner implementation looks like:
20
-
21
- ```ruby
22
- class SomeCloner < Clowne::Cloner
23
- adapter :active_record # or adapter Clowne::Adapters::ActiveRecord
24
- # some implementation ...
25
- end
26
- ```
27
-
28
- You can configure the default adapter for cloners:
29
-
30
- ```ruby
31
- # put to initializer
32
- # e.g. config/initializers/clowne.rb
33
- Clowne.default_adapter = :active_record
34
- ```
35
-
36
- and skip explicit adapter declaration
37
-
38
- ```ruby
39
- class SomeCloner < Clowne::Cloner
40
- # some implementation ...
41
- end
42
- ```
43
- See the list of [available adapters](supported_adapters.md).
44
-
45
- ## Basic Example
46
-
47
- Assume that you have the following model:
48
-
49
- ```ruby
50
- class User < ActiveRecord::Base
51
- # create_table :users do |t|
52
- # t.string :login
53
- # t.string :email
54
- # t.timestamps null: false
55
- # end
56
-
57
- has_one :profile
58
- has_many :posts
59
- end
60
-
61
- class Profile < ActiveRecord::Base
62
- # create_table :profiles do |t|
63
- # t.string :name
64
- # end
65
- end
66
-
67
- class Post < ActiveRecord::Base
68
- # create_table :posts
69
- end
70
- ```
71
-
72
- Let's declare our cloners first:
73
-
74
- ```ruby
75
- class UserCloner < Clowne::Cloner
76
- adapter :active_record
77
-
78
- include_association :profile, clone_with: SpecialProfileCloner
79
- include_association :posts
80
-
81
- nullify :login
82
-
83
- # params here is an arbitrary Hash passed into cloner
84
- finalize do |_source, record, **params|
85
- record.email = params[:email]
86
- end
87
- end
88
-
89
- class SpecialProfileCloner < Clowne::Cloner
90
- adapter :active_record
91
-
92
- nullify :name
93
- end
94
- ```
95
-
96
- Now you can use `UserCloner` to clone existing records:
97
-
98
- ```ruby
99
- user = User.last
100
- # => <#User id: 1, login: 'clown', email: 'clown@circus.example.com'>
101
-
102
- operation = UserCloner.call(user, email: "fake@example.com")
103
- # => <#Clowne::Utils::Operation...>
104
-
105
- operation.to_record
106
- # => <#User id: nil, login: nil, email: 'fake@example.com'>
107
-
108
- operation.persist!
109
- # => true
110
-
111
- cloned = operation.to_record
112
- # => <#User id: 2, login: nil, email: 'fake@example.com'>
113
-
114
- cloned.login
115
- # => nil
116
- cloned.email
117
- # => "fake@example.com"
118
-
119
- # associations:
120
- cloned.posts.count == user.posts.count
121
- # => true
122
- cloned.profile.name
123
- # => nil
124
- ```
125
-
126
- ## Overview
127
-
128
- In [the basic example](#basic-example), you can see that Clowne consists of flexible DSL which is used in a class inherited of `Clowne::Cloner`.
129
-
130
- You can combinate this DSL via [`traits`](traits.md) and make a cloning plan which exactly you want.
131
-
132
- **We strongly recommend [`write tests`](testing.md) to cover resulting cloner logic**
133
-
134
- Cloner class returns [`Operation`](operation.md) instance as a result of cloning. The operation provides methods to save cloned record. You can wrap this call to a transaction if it is necessary.
135
-
136
- ### Execution Order
137
-
138
- The order of cloning actions depends on the adapter (i.e., could be customized).
139
-
140
- All built-in adapters have the same order and what happens when you call `Operation#persist`:
141
- - init clone (see [`init_as`](init_as.md)) (empty by default)
142
- - [`clone associations`](include_association.md)
143
- - [`nullify`](nullify.md) attributes
144
- - run [`finalize`](finalize.md) blocks. _The order of [`finalize`](finalize.md) blocks is the order they've been written._
145
- - run [`after_clone`](after_clone.md) callbacks
146
- - __SAVE CLONED RECORD__
147
- - run [`after_persist`](after_persist.md) callbacks
148
-
149
- ## Motivation & Alternatives
150
-
151
- ### Why did we decide to build our own cloning gem instead of using the existing solutions?
152
-
153
- First, the existing solutions turned out not to be stable and flexible enough for us.
154
-
155
- Secondly, they are Rails-only (or, more precisely, ActiveRecord-only).
156
-
157
- Nevertheless, thanks to [amoeba](https://github.com/amoeba-rb/amoeba) and [deep_cloneable](https://github.com/moiristo/deep_cloneable) for inspiration.
158
-
159
- For ActiveRecord we support amoeba-like [in-model configuration](active_record.md) and you can add missing DSL declarations yourself [easily](customization.md).
160
-
161
- We also provide an ability to specify cloning [configuration in-place](inline_configuration.md) like `deep_clonable` does.
162
-
163
- So, we took the best of these too and brought to the outside-of-Rails world.
164
-
165
- ### Why build a gem to clone models at all?
166
-
167
- That's a good question. Of course, you can write plain old Ruby services do handle the cloning logic. But for complex models hierarchies, this approach has major disadvantages: high code complexity and lack of re-usability.
168
-
169
- The things become even worse when you deal with STI models and different cloning contexts.
170
-
171
- That's why we decided to build a specific cloning tool.
@@ -1,33 +0,0 @@
1
- # Implicit Cloner
2
-
3
- When [cloning associations](include_association.md) Clowne tries to infer an appropriate cloner class for the records (unless `clone_with` specified).
4
-
5
- It relies on the naming convention: `MyModel` -> `MyModelCloner`.
6
-
7
- Consider an example:
8
-
9
- ```ruby
10
- class User < ActiveRecord::Base
11
- has_one :profile
12
- end
13
-
14
- class UserCloner < Clowne::Cloner
15
- include_association :profile
16
- end
17
-
18
- class ProfileCloner < Clowne::Cloner
19
- finalize do |source, record|
20
- record.name = "Clone of #{source.name}"
21
- end
22
- end
23
-
24
- user = User.last
25
- user.profile.name
26
- #=> "Bimbo"
27
-
28
- cloned = UserCloner.call(user).to_record
29
- cloned.profile.name
30
- # => "Clone of Bimbo"
31
- ```
32
-
33
- **NOTE:** when using [in-model cloner](active_record.md) for ActiveRecord it is used by default.
@@ -1,133 +0,0 @@
1
- # Include Association
2
-
3
- Use this declaration to clone model's associations:
4
-
5
- ```ruby
6
- class User < ActiveRecord::Base
7
- has_one :profile
8
- end
9
-
10
- class UserCloner < Clowne::Cloner
11
- include_association :profile
12
- end
13
- ```
14
-
15
- Looks pretty simple, right? But that's not all we may offer you! :)
16
-
17
- The declaration supports additional arguments:
18
-
19
- ```ruby
20
- include_association name, scope, options
21
- ```
22
-
23
- ### Supported Associations
24
-
25
- Adapter |1:1 |*:1 | 1:M | M:M |
26
- ------------------------------------------|------------|------------|-------------|-------------------------|
27
- [Active Record](active_record) | has_one | belongs_to | has_many | has_and_belongs_to|
28
- [Sequel](sequel) | one_to_one | - | one_to_many | many_to_many |
29
-
30
- ## Scope
31
-
32
- Scope can be a:
33
- - `Symbol` - named scope.
34
- - `Proc` - custom scope (supports parameters).
35
-
36
- Example:
37
-
38
- ```ruby
39
- class User < ActiveRecord::Base
40
- has_many :accounts
41
- has_many :posts
42
- end
43
-
44
- class Account < ActiveRecord::Base
45
- scope :active, -> { where(active: true) }
46
- end
47
-
48
- class Post < ActiveRecord::Base
49
- # t.string :status
50
- end
51
-
52
- class UserCloner < Clowne::Cloner
53
- include_association :accounts, :active
54
- include_association :posts, ->(params) { where(state: params[:state]) }
55
- end
56
-
57
- # Clone only draft posts
58
- UserCloner.call(user, state: :draft).to_record
59
- # => <#User id: nil, ... >
60
- ```
61
-
62
- ## Options
63
-
64
- The following options are available:
65
- - `:clone_with` - use custom cloner\*
66
- - `:traits` - define special traits.
67
-
68
- \* **NOTE:** the same cloner class would be used for **all children**
69
-
70
- Example:
71
-
72
- ```ruby
73
- class User < ActiveRecord::Base
74
- has_many :posts
75
- end
76
-
77
- class Post < ActiveRecord::Base
78
- # t.string :title
79
- has_many :tags
80
- end
81
- ```
82
-
83
- ```ruby
84
- class PostSpecialCloner < Clowne::Cloner
85
- nullify :title
86
-
87
- trait :with_tags do
88
- include_association :tags
89
- end
90
- end
91
-
92
- class UserCloner < Clowne::Cloner
93
- adapter :active_record
94
-
95
- include_association :posts, clone_with: PostSpecialCloner
96
- # or clone user's posts with tags!
97
- # include_association :posts, clone_with: PostSpecialCloner, traits: :with_tags
98
- end
99
-
100
- UserCloner.call(user).to_record
101
- # => <#User id: nil, ... >
102
- ```
103
-
104
- **NOTE**: if custom cloner is not defined, Clowne tries to infer the [implicit cloner](implicit_cloner.md).
105
-
106
- ## Nested parameters
107
-
108
- Follow to [documentation page](parameters.md).
109
-
110
- ## Include multiple associations
111
-
112
- You can include multiple associations at once too:
113
-
114
- ```ruby
115
- class User < ActiveRecord::Base
116
- has_many :accounts
117
- has_many :posts
118
- end
119
-
120
- class UserCloner < Clowne::Cloner
121
- adapter :active_record
122
-
123
- include_associations :accounts, :posts
124
- end
125
- ```
126
-
127
- **NOTE:** in that case, it's not possible to provide custom scopes and options.
128
-
129
- ### Belongs To association
130
-
131
- You can include belongs_to association, but will do it carefully.
132
- If you have loop by relations in your models, when you clone it will raise SystemStackError.
133
- Check this [test](https://github.com/palkan/clowne/blob/master/spec/clowne/integrations/active_record_belongs_to_spec.rb) for instance.
data/docs/index.html DELETED
@@ -1,29 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Document</title>
6
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
- <meta name="description" content="Description">
8
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
9
- <link rel="stylesheet" href="assets/vue.css">
10
- <link rel="stylesheet" href="assets/styles.css">
11
- </head>
12
- <body>
13
- <div id="app"></div>
14
- <script>
15
- window.$docsify = {
16
- name: 'Clowne',
17
- repo: 'https://github.com/clowne-rb/clowne',
18
- loadSidebar: true,
19
- subMaxLevel: 2,
20
- auto2top: true,
21
- search: {
22
- namespace: 'clowne'
23
- }
24
- }
25
- </script>
26
- <script src="assets/docsify.min.js"></script>
27
- <script src="assets/prism-ruby.min.js"></script>
28
- </body>
29
- </html>
data/docs/init_as.md DELETED
@@ -1,40 +0,0 @@
1
- # Initialize Cloning Target
2
-
3
- You can override the default Clowne method which generates a _plain_ copy of a source object.
4
- By default, Clowne initiates the cloned record using a `#dup` method.
5
-
6
- For example, Cloners could be used not only to generate _fresh_ new models but to apply some transformations to the existing record:
7
-
8
-
9
- ```ruby
10
- class User < ApplicationRecord
11
- has_one :profile
12
- has_many :posts
13
- end
14
-
15
- class UserCloner < Clowne::Cloner
16
- adapter :active_record
17
-
18
- include_association :profile
19
-
20
- trait :copy_settings do
21
- # Use a `target` for all the actions
22
- init_as { |_source, target:| target }
23
- end
24
- end
25
-
26
- jack = User.find_by(email: "jack@evl.ms")
27
- # => <#User id: 1, ...>
28
- jack.create_profile(name: "Jack")
29
- # => <#Profile id: 1, name: 'Jack', ...>
30
-
31
- john = User.find_by(email: "john@evl.ms")
32
- # => <#User id: 2, ...>
33
-
34
- # we want to clone Jack's profile to John's user,
35
- # without creating a new one
36
- john_with_profile = UserCloner.call(jack, traits: :copy_settings, target: john).to_record
37
- # => <#User id: 2, ...>
38
- john_with_profile.profile
39
- #=> <#Profile id: nil, name: 'Jack',...>
40
- ```
@@ -1,37 +0,0 @@
1
- # Inline Configuration
2
-
3
- You can also enhance the cloner configuration inline (i.e., add declarations dynamically):
4
-
5
- ```ruby
6
- operation = UserCloner.call(User.last) do
7
- exclude_association :profile
8
-
9
- finalize do |source, record|
10
- record.email = "clone_of_#{source.email}"
11
- end
12
- end
13
-
14
- cloned = operation.to_record
15
-
16
- cloned.email
17
- # => "clone_of_john@example.com"
18
-
19
- # associations:
20
- cloned.posts.size == User.last.posts.size
21
- # => true
22
- cloned.profile
23
- # => nil
24
- ```
25
-
26
- Inline enhancement doesn't affect the _global_ configuration so that you can use it without any fear.
27
-
28
- Thus it's also possible to clone objects without any cloner classes at all by using `Clowne::Cloner`:
29
-
30
- ```ruby
31
- cloned = Clowne::Cloner.call(user) do
32
- # anything you want!
33
- end.to_record
34
-
35
- cloned
36
- # => <#User..
37
- ```