clowne 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +0 -2
  4. data/lib/clowne/adapters/active_record/associations/noop.rb +1 -1
  5. data/lib/clowne/adapters/base/association.rb +1 -1
  6. data/lib/clowne/adapters/sequel/associations/one_to_many.rb +0 -4
  7. data/lib/clowne/ext/record_key.rb +1 -1
  8. data/lib/clowne/version.rb +1 -1
  9. metadata +7 -113
  10. data/.codeclimate.yml +0 -7
  11. data/.gitattributes +0 -1
  12. data/.github/workflows/rspec-jruby.yml +0 -33
  13. data/.github/workflows/rspec-truffle.yml +0 -35
  14. data/.github/workflows/rspec.yml +0 -51
  15. data/.github/workflows/rubocop.yml +0 -20
  16. data/.gitignore +0 -16
  17. data/.rspec +0 -3
  18. data/.rubocop.yml +0 -29
  19. data/.rufo +0 -3
  20. data/Gemfile +0 -20
  21. data/Rakefile +0 -8
  22. data/bin/console +0 -14
  23. data/bin/setup +0 -8
  24. data/clowne.gemspec +0 -36
  25. data/docs/.nojekyll +0 -0
  26. data/docs/.rubocop.yml +0 -15
  27. data/docs/CNAME +0 -1
  28. data/docs/README.md +0 -131
  29. data/docs/_sidebar.md +0 -25
  30. data/docs/active_record.md +0 -33
  31. data/docs/after_clone.md +0 -53
  32. data/docs/after_persist.md +0 -77
  33. data/docs/architecture.md +0 -138
  34. data/docs/assets/docsify.min.js +0 -1
  35. data/docs/assets/prism-ruby.min.js +0 -1
  36. data/docs/assets/styles.css +0 -348
  37. data/docs/assets/vue.css +0 -1
  38. data/docs/clone_mapper.md +0 -59
  39. data/docs/customization.md +0 -63
  40. data/docs/exclude_association.md +0 -61
  41. data/docs/finalize.md +0 -31
  42. data/docs/from_v02_to_v1.md +0 -83
  43. data/docs/getting_started.md +0 -171
  44. data/docs/implicit_cloner.md +0 -33
  45. data/docs/include_association.md +0 -133
  46. data/docs/index.html +0 -29
  47. data/docs/init_as.md +0 -40
  48. data/docs/inline_configuration.md +0 -37
  49. data/docs/nullify.md +0 -33
  50. data/docs/operation.md +0 -55
  51. data/docs/parameters.md +0 -112
  52. data/docs/sequel.md +0 -50
  53. data/docs/supported_adapters.md +0 -10
  54. data/docs/testing.md +0 -194
  55. data/docs/traits.md +0 -25
  56. data/gemfiles/activerecord42.gemfile +0 -9
  57. data/gemfiles/jruby.gemfile +0 -10
  58. data/gemfiles/railsmaster.gemfile +0 -10
  59. data/lib/clowne/ext/yield_self_then.rb +0 -25
@@ -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
- ```
data/docs/nullify.md DELETED
@@ -1,33 +0,0 @@
1
- # Nullify Attributes
2
-
3
- To set a bunch of attributes to `nil` you can use the `nullify` declaration:
4
-
5
- ```ruby
6
- class User < ActiveRecord::Base
7
- # t.string :name
8
- # t.string :surname
9
- # t.string :email
10
- end
11
-
12
- class UserCloner < Clowne::Cloner
13
- nullify :name, :email
14
-
15
- trait :nullify_surname do
16
- nullify :surname
17
- end
18
- end
19
-
20
- clone = UserCloner.call(user).to_record
21
- clone.name.nil?
22
- # => true
23
- clone.email.nil?
24
- # => true
25
- clone.surname.nil?
26
- # => false
27
-
28
- clone2 = UserCloner.call(user, traits: :nullify_surname).to_record
29
- clone2.name.nil?
30
- # => true
31
- clone2.surname.nil?
32
- # => true
33
- ```
data/docs/operation.md DELETED
@@ -1,55 +0,0 @@
1
- # Operation
2
-
3
- Since version 1.0 Clowne has been returning specific result object instead of a raw cloned object. It has allowed unifying interface between adapters and has opened an opportunity to implement new features. We call this object `Operation`.
4
-
5
- An instance of `Operation` has a very clear interface:
6
-
7
- ```ruby
8
- class User < ActiveRecord::Base; end
9
-
10
- class UserCloner < Clowne::Cloner
11
- nullify :email
12
-
13
- after_persist do |_origin, cloned, **|
14
- cloned.update(email: "evl-#{cloned.id}.ms")
15
- end
16
- end
17
-
18
- user = User.create(email: "evl.ms")
19
- # => <#User id: 1, email: 'evl.ms', ...>
20
-
21
- operation = UserCloner.call(user)
22
-
23
- # Return resulted (non saved) object:
24
- operation.to_record
25
- # => <#User id: nil, email: nil, ...>
26
-
27
- # Save cloned object and call after_persist callbacks:
28
- operation.persist # or operation.persist!
29
- # => true
30
-
31
- operation.to_record
32
- # => <#User id: 2, email: 'evl-2.ms', ...>
33
-
34
- # Call only after_persist callbacks:
35
- user2 = operation.to_record
36
- # => <#User id: 2, email: 'evl-2.ms', ...>
37
- user2.update(email: "admin@example.com")
38
- # => <#User id: 2, email: 'admin@example.com' ...>
39
- operation.run_after_persist
40
- # => <#User id: 2, email: 'evl-2.ms', ...>
41
- ```
42
-
43
- The last example is weird, but it can be helpful when you need to execute `save` (or `save!`) separately from `after_persist` callbacks:
44
-
45
- ```ruby
46
- operation = UserClone.call(user)
47
-
48
- # Wrap main cloning into the transaction
49
- ActiveRecord::Base.transaction do
50
- operation.to_record.save!
51
- end
52
-
53
- # And after that execute after_persist without transaction
54
- operation.run_after_persist
55
- ```
data/docs/parameters.md DELETED
@@ -1,112 +0,0 @@
1
- # Parameters
2
-
3
- Clowne provides parameters for make your cloning logic more flexible. You can see their using in [`include_association`](include_association.md#scope) and [`finalize`](finalize.md) documentation pages.
4
-
5
- Example:
6
-
7
- ```ruby
8
- class UserCloner < Clowne::Cloner
9
- include_association :posts, ->(params) { where(state: params[:state]) }
10
-
11
- finalize do |_source, record, **params|
12
- record.email = params[:email]
13
- end
14
- end
15
-
16
- operation = UserCloner.call(user, state: :draft, email: "cloned@example.com")
17
- cloned = operation.to_record
18
- cloned.email
19
- # => 'cloned@example.com'
20
- ```
21
-
22
- ## Potential Problems
23
-
24
- Clowne is born as a part of our big project and we use it for cloning really deep object relations. When we started to use params and forwarding them between parent-child cloners we got a nasty bugs.
25
-
26
- As result we strongly recommend to use ruby keyword arguments instead of params hash:
27
-
28
- ```ruby
29
- # Bad
30
- finalize do |_source, record, **params|
31
- record.email = params[:email]
32
- end
33
-
34
- # Good
35
- finalize do |_source, record, email:, **|
36
- record.email = email
37
- end
38
- ```
39
-
40
- ## Nested Parameters
41
-
42
- Also we implemented control over the parameters for cloning associations (you can read more [here](https://github.com/clowne-rb/clowne/issues/15)).
43
-
44
- Let's explain what the difference:
45
-
46
- ```ruby
47
- class UserCloner < Clowne::Cloner
48
- # Don't pass parameters to associations
49
- trait :default do
50
- include_association :profile
51
- # equal to include_association :profile, params: false
52
- end
53
-
54
- # Pass all parameters to associations
55
- trait :all_params do
56
- include_association :profile, params: true
57
- end
58
-
59
- # Filter parameters by key.
60
- # Notice: value by key must be a Hash.
61
-
62
- trait :by_key do
63
- include_association :profile, params: :profile
64
- end
65
-
66
- # Execute custom block with params as argument
67
- trait :by_block do
68
- include_association :profile, params: Proc.new do |params|
69
- params[:profile].map { |k, v| [k, v.upcase] }.to_h
70
- end
71
- end
72
-
73
- # Execute custom block with params and parent record as arguments
74
- trait :by_block_with_parent do
75
- include_association :profile, params: Proc.new do |params, user|
76
- {
77
- name: params[:profile][:name],
78
- email: user.email
79
- }
80
- end
81
- end
82
- end
83
-
84
- class ProfileCloner < Clowne::Cloner
85
- finalize do |_source, record, **params|
86
- record.jsonb_field = params
87
- end
88
- end
89
-
90
- # Execute:
91
-
92
- def get_profile_jsonb(user, trait)
93
- params = {profile: {name: "John", surname: "Cena"}}
94
- cloned = UserCloner.call(user, traits: trait, **params).to_record
95
- cloned.profile.jsonb_field
96
- end
97
-
98
- get_profile_jsonb(user, :default)
99
- # => {}
100
-
101
- get_profile_jsonb(user, :all_params)
102
- # => { profile: { name: 'John', surname: 'Cena' } }
103
-
104
- get_profile_jsonb(user, :by_key)
105
- # => { name: 'John', surname: 'Cena' }
106
-
107
- get_profile_jsonb(user, :by_block)
108
- # => { name: 'JOHN', surname: 'CENA' }
109
-
110
- get_profile_jsonb(user, :by_block_with_parent)
111
- # => { name: 'JOHN', email: user.email }
112
- ```
data/docs/sequel.md DELETED
@@ -1,50 +0,0 @@
1
- # Sequel
2
-
3
- Under the hood, Clowne uses Sequel [`NestedAttributes` plugin](http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/NestedAttributes.html) for cloning source's associations, and you need to configure it.
4
-
5
- Example:
6
-
7
- ```ruby
8
- class UserCloner < Clowne::Cloner
9
- adapter :sequel
10
-
11
- include_association :account
12
- end
13
-
14
- class User < Sequel::Model
15
- # Configure NestedAttributes plugin
16
- plugin :nested_attributes
17
-
18
- one_to_one :account
19
- nested_attributes :account
20
- end
21
- ```
22
-
23
- and get cloned user
24
-
25
- ```ruby
26
- user = User.last
27
- operation = UserCloner.call(user)
28
- # => <#Clowne::Adapters::Sequel::Operation...>
29
- cloned = operation.to_record
30
- # => <#User id: nil, ...>
31
- cloned.new?
32
- # => true
33
- ```
34
-
35
- or you can save it immediately
36
-
37
- ```ruby
38
- user = User.last
39
- # => <#User id: 1, ...>
40
- operation = UserCloner.call(user)
41
- # => <#Clowne::Adapters::Sequel::Operation...>
42
- operation.persist
43
- # => true
44
- cloned = operation.to_record
45
- # => <#User id: 2, ...>
46
- cloned.new?
47
- # => false
48
- ```
49
-
50
- If you try to clone association without `NestedAttributes` plugin, Clowne will skip this declaration.
@@ -1,10 +0,0 @@
1
- # Supported Adapters
2
-
3
- Clowne supports the following ORM adapters (and associations):
4
-
5
- Adapter |1:1 | 1:M | M:M |
6
- ---------------------------------------------------|------------|-------------|-------------------------|
7
- [:active_record](active_record) | has_one | has_many | has_and_belongs_to_many |
8
- [:sequel](sequel) | one_to_one | one_to_many | many_to_many |
9
-
10
- For more information see the corresponding adapter documentation.