clowne 1.4.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 (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.