clowne 1.3.0 → 1.5.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.
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
- ```