clowne 0.2.0 → 1.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.travis.yml +6 -3
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -1
- data/README.md +30 -9
- data/clowne.gemspec +4 -3
- data/docs/active_record.md +2 -2
- data/docs/after_persist.md +80 -0
- data/docs/basic_example.md +22 -5
- data/docs/clone_mapper.md +62 -0
- data/docs/customization.md +2 -1
- data/docs/exclude_association.md +5 -4
- data/docs/finalize.md +2 -2
- data/docs/from_v02_to_v1.md +91 -0
- data/docs/implicit_cloner.md +1 -1
- data/docs/include_association.md +4 -4
- data/docs/init_as.md +10 -2
- data/docs/inline_configuration.md +4 -2
- data/docs/installation.md +31 -1
- data/docs/nullify.md +2 -2
- data/docs/operation.md +58 -0
- data/docs/overview.md +24 -0
- data/docs/parameters.md +5 -4
- data/docs/sequel.md +15 -18
- data/docs/supported_adapters.md +2 -2
- data/docs/testing.md +7 -5
- data/docs/web/README.md +6 -0
- data/docs/web/core/Footer.js +5 -9
- data/docs/web/i18n/en.json +8 -4
- data/docs/web/pages/en/index.js +1 -1
- data/docs/web/sidebars.json +10 -4
- data/docs/web/siteConfig.js +6 -4
- data/docs/web/static/css/custom.css +16 -10
- data/gemfiles/activerecord42.gemfile +3 -1
- data/gemfiles/jruby.gemfile +2 -0
- data/gemfiles/railsmaster.gemfile +2 -0
- data/lib/clowne.rb +3 -0
- data/lib/clowne/adapters/active_record.rb +2 -3
- data/lib/clowne/adapters/active_record/associations/base.rb +0 -4
- data/lib/clowne/adapters/active_record/associations/has_one.rb +2 -1
- data/lib/clowne/adapters/active_record/resolvers/association.rb +38 -0
- data/lib/clowne/adapters/base.rb +42 -43
- data/lib/clowne/adapters/base/association.rb +24 -15
- data/lib/clowne/adapters/registry.rb +49 -0
- data/lib/clowne/adapters/sequel.rb +10 -6
- data/lib/clowne/adapters/sequel/associations/base.rb +8 -4
- data/lib/clowne/adapters/sequel/associations/many_to_many.rb +6 -2
- data/lib/clowne/adapters/sequel/associations/one_to_many.rb +7 -2
- data/lib/clowne/adapters/sequel/associations/one_to_one.rb +7 -2
- data/lib/clowne/adapters/sequel/operation.rb +32 -0
- data/lib/clowne/adapters/sequel/record_wrapper.rb +0 -16
- data/lib/clowne/adapters/sequel/resolvers/after_persist.rb +22 -0
- data/lib/clowne/adapters/sequel/resolvers/association.rb +51 -0
- data/lib/clowne/adapters/sequel/specifications/after_persist_does_not_support.rb +15 -0
- data/lib/clowne/cloner.rb +27 -20
- data/lib/clowne/declarations.rb +2 -1
- data/lib/clowne/declarations/after_persist.rb +21 -0
- data/lib/clowne/declarations/finalize.rb +1 -0
- data/lib/clowne/declarations/include_association.rb +2 -1
- data/lib/clowne/declarations/init_as.rb +1 -0
- data/lib/clowne/declarations/nullify.rb +1 -0
- data/lib/clowne/declarations/trait.rb +1 -0
- data/lib/clowne/dsl.rb +9 -0
- data/lib/clowne/ext/lambda_as_proc.rb +1 -0
- data/lib/clowne/ext/record_key.rb +12 -0
- data/lib/clowne/ext/yield_self_then.rb +25 -0
- data/lib/clowne/planner.rb +6 -3
- data/lib/clowne/resolvers/after_persist.rb +18 -0
- data/lib/clowne/resolvers/finalize.rb +12 -0
- data/lib/clowne/resolvers/init_as.rb +13 -0
- data/lib/clowne/resolvers/nullify.rb +15 -0
- data/lib/clowne/rspec/helpers.rb +1 -0
- data/lib/clowne/utils/clone_mapper.rb +26 -0
- data/lib/clowne/utils/operation.rb +83 -0
- data/lib/clowne/utils/options.rb +39 -0
- data/lib/clowne/utils/params.rb +64 -0
- data/lib/clowne/utils/plan.rb +90 -0
- data/lib/clowne/version.rb +1 -1
- metadata +44 -18
- data/docs/configuration.md +0 -29
- data/docs/execution_order.md +0 -14
- data/docs/web/static/fonts/StemText.woff +0 -0
- data/docs/web/static/fonts/StemTextBold.woff +0 -0
- data/lib/clowne/adapters/active_record/association.rb +0 -34
- data/lib/clowne/adapters/base/finalize.rb +0 -19
- data/lib/clowne/adapters/base/init_as.rb +0 -21
- data/lib/clowne/adapters/base/nullify.rb +0 -19
- data/lib/clowne/adapters/sequel/association.rb +0 -47
- data/lib/clowne/params.rb +0 -62
- data/lib/clowne/plan.rb +0 -83
data/docs/implicit_cloner.md
CHANGED
data/docs/include_association.md
CHANGED
@@ -51,8 +51,8 @@ class UserCloner < Clowne::Cloner
|
|
51
51
|
end
|
52
52
|
|
53
53
|
# Clone only draft posts
|
54
|
-
UserCloner.call(user, state: :draft)
|
55
|
-
# => <#User...
|
54
|
+
UserCloner.call(user, state: :draft).to_record
|
55
|
+
# => <#User id: nil, ... >
|
56
56
|
```
|
57
57
|
|
58
58
|
## Options
|
@@ -93,8 +93,8 @@ class UserCloner < Clowne::Cloner
|
|
93
93
|
# include_association :posts, clone_with: PostSpecialCloner, traits: :with_tags
|
94
94
|
end
|
95
95
|
|
96
|
-
UserCloner.call(user)
|
97
|
-
# => <#User...
|
96
|
+
UserCloner.call(user).to_record
|
97
|
+
# => <#User id: nil, ... >
|
98
98
|
```
|
99
99
|
|
100
100
|
**NOTE**: if custom cloner is not defined, Clowne tries to infer the [implicit cloner](implicit_cloner.md).
|
data/docs/init_as.md
CHANGED
@@ -28,9 +28,17 @@ class UserCloner < Clowne::Cloner
|
|
28
28
|
end
|
29
29
|
|
30
30
|
jack = User.find_by(email: 'jack@evl.ms')
|
31
|
+
# => <#User id: 1, ...>
|
32
|
+
jack.create_profile(name: 'Jack')
|
33
|
+
# => <#Profile id: 1, name: 'Jack', ...>
|
34
|
+
|
31
35
|
john = User.find_by(email: 'john@evl.ms')
|
36
|
+
# => <#User id: 2, ...>
|
32
37
|
|
33
|
-
# we want to clone Jack's profile
|
38
|
+
# we want to clone Jack's profile to John's user,
|
34
39
|
# without creating a new one
|
35
|
-
UserCloner.call(jack, traits: :copy_settings, target: john)
|
40
|
+
john_with_profile = UserCloner.call(jack, traits: :copy_settings, target: john).to_record
|
41
|
+
# => <#User id: 2, ...>
|
42
|
+
john_with_profile.profile
|
43
|
+
#=> <#Profile id: nil, name: 'Jack',...>
|
36
44
|
```
|
@@ -6,7 +6,7 @@ title: Inline Configuration
|
|
6
6
|
You can also enhance the cloner configuration inline (i.e., add declarations dynamically):
|
7
7
|
|
8
8
|
```ruby
|
9
|
-
|
9
|
+
operation = UserCloner.call(User.last) do
|
10
10
|
exclude_association :profile
|
11
11
|
|
12
12
|
finalize do |source, record|
|
@@ -14,6 +14,8 @@ cloned = UserCloner.call(User.last) do
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
cloned = operation.to_record
|
18
|
+
|
17
19
|
cloned.email
|
18
20
|
# => "clone_of_john@example.com"
|
19
21
|
|
@@ -31,7 +33,7 @@ Thus it's also possible to clone objects without any cloner classes at all by us
|
|
31
33
|
```ruby
|
32
34
|
cloned = Clowne::Cloner.call(user) do
|
33
35
|
# anything you want!
|
34
|
-
end
|
36
|
+
end.to_record
|
35
37
|
|
36
38
|
cloned
|
37
39
|
# => <#User..
|
data/docs/installation.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
---
|
2
2
|
id: installation
|
3
|
-
title: Installation
|
3
|
+
title: Installation & Configuration
|
4
4
|
---
|
5
5
|
|
6
|
+
## Installation
|
7
|
+
|
6
8
|
To install Clowne with RubyGems:
|
7
9
|
|
8
10
|
```ruby
|
@@ -14,3 +16,31 @@ Or add this line to your application's Gemfile:
|
|
14
16
|
```ruby
|
15
17
|
gem 'clowne'
|
16
18
|
```
|
19
|
+
|
20
|
+
## Configuration
|
21
|
+
|
22
|
+
Basic cloner implementation looks like:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class SomeCloner < Clowne::Cloner
|
26
|
+
adapter :active_record # or adapter Clowne::Adapters::ActiveRecord
|
27
|
+
# some implementation ...
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
You can configure the default adapter for cloners:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# put to initializer
|
35
|
+
# e.g. config/initializers/clowne.rb
|
36
|
+
Clowne.default_adapter = :active_record
|
37
|
+
```
|
38
|
+
|
39
|
+
and skip explicit adapter declaration
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class SomeCloner < Clowne::Cloner
|
43
|
+
# some implementation ...
|
44
|
+
end
|
45
|
+
```
|
46
|
+
See the list of [available adapters](supported_adapters.md).
|
data/docs/nullify.md
CHANGED
@@ -21,7 +21,7 @@ class UserCloner < Clowne::Cloner
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
clone = UserCloner.call(user)
|
24
|
+
clone = UserCloner.call(user).to_record
|
25
25
|
clone.name.nil?
|
26
26
|
# => true
|
27
27
|
clone.email.nil?
|
@@ -29,7 +29,7 @@ clone.email.nil?
|
|
29
29
|
clone.surname.nil?
|
30
30
|
# => false
|
31
31
|
|
32
|
-
clone2 = UserCloner.call(user, traits: :nullify_surname)
|
32
|
+
clone2 = UserCloner.call(user, traits: :nullify_surname).to_record
|
33
33
|
clone2.name.nil?
|
34
34
|
# => true
|
35
35
|
clone2.surname.nil?
|
data/docs/operation.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
---
|
2
|
+
id: operation
|
3
|
+
title: Operation
|
4
|
+
---
|
5
|
+
|
6
|
+
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`.
|
7
|
+
|
8
|
+
An instance of `Operation` has a very clear interface:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class User < ActiveRecord::Base; end
|
12
|
+
|
13
|
+
class UserCloner < Clowne::Cloner
|
14
|
+
nullify :email
|
15
|
+
|
16
|
+
after_persist do |_origin, cloned, **|
|
17
|
+
cloned.update_attributes(email: "evl-#{cloned.id}.ms")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
user = User.create(email: 'evl.ms')
|
22
|
+
# => <#User id: 1, email: 'evl.ms', ...>
|
23
|
+
|
24
|
+
operation = UserCloner.call(user)
|
25
|
+
|
26
|
+
# Return resulted (non saved) object:
|
27
|
+
operation.to_record
|
28
|
+
# => <#User id: nil, email: nil, ...>
|
29
|
+
|
30
|
+
# Save cloned object and call after_persist callbacks:
|
31
|
+
operation.persist # or operation.persist!
|
32
|
+
# => true
|
33
|
+
|
34
|
+
operation.to_record
|
35
|
+
# => <#User id: 2, email: 'evl-2.ms', ...>
|
36
|
+
|
37
|
+
# Call only after_persist callbacks:
|
38
|
+
user2 = operation.to_record
|
39
|
+
# => <#User id: 2, email: 'evl-2.ms', ...>
|
40
|
+
user2.update_attributes(email: 'admin@example.com')
|
41
|
+
# => <#User id: 2, email: 'admin@example.com' ...>
|
42
|
+
operation.run_after_persist
|
43
|
+
# => <#User id: 2, email: 'evl-2.ms', ...>
|
44
|
+
```
|
45
|
+
|
46
|
+
The last example is weird, but it can be helpful when you need to execute `save` (or `save!`) separately from `after_persist` callbacks:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
operation = UserClone.call(user)
|
50
|
+
|
51
|
+
# Wrap main cloning into the transaction
|
52
|
+
ActiveRecord::Base.transaction do
|
53
|
+
operation.to_record.save!
|
54
|
+
end
|
55
|
+
|
56
|
+
# And after that execute after_persist without transaction
|
57
|
+
operation.run_after_persist
|
58
|
+
```
|
data/docs/overview.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
id: overview
|
3
|
+
title: Overview
|
4
|
+
---
|
5
|
+
|
6
|
+
In [the basic example](basic_example.md), you can see that Clowne consists of flexible DSL which is used in a class inherited of `Clowne::Cloner`.
|
7
|
+
|
8
|
+
You can combinate this DSL via [`traits`](traits.md) and make a cloning plan which exactly you want.
|
9
|
+
|
10
|
+
**We strongly recommend [`write tests`](testing.md) to cover resulting cloner logic**
|
11
|
+
|
12
|
+
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.
|
13
|
+
|
14
|
+
## Execution Order
|
15
|
+
|
16
|
+
The order of cloning actions depends on the adapter (i.e., could be customized).
|
17
|
+
|
18
|
+
All built-in adapters have the same order and what happens when you call `Operation#persist`:
|
19
|
+
- init clone (see [`init_as`](init_as.md)) (empty by default)
|
20
|
+
- [`clone associations`](include_association.md)
|
21
|
+
- [`nullify`](nullify.md) attributes
|
22
|
+
- run [`finalize`](finalize.md) blocks. _The order of [`finalize`](finalize.md) blocks is the order they've been written._
|
23
|
+
- __SAVE CLONED RECORD__
|
24
|
+
- run [`after_persist`](after_persist.md) callbacks
|
data/docs/parameters.md
CHANGED
@@ -16,7 +16,8 @@ class UserCloner < Clowne::Cloner
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
operation = UserCloner.call(user, state: :draft, email: 'cloned@example.com')
|
20
|
+
cloned = operation.to_record
|
20
21
|
cloned.email
|
21
22
|
# => 'cloned@example.com'
|
22
23
|
```
|
@@ -54,7 +55,7 @@ class UserCloner < Clowne::Cloner
|
|
54
55
|
end
|
55
56
|
|
56
57
|
# Pass all parameters to associations
|
57
|
-
trait :
|
58
|
+
trait :all_params do
|
58
59
|
include_association :profile, params: true
|
59
60
|
end
|
60
61
|
|
@@ -93,14 +94,14 @@ end
|
|
93
94
|
|
94
95
|
def get_profile_jsonb(user, trait)
|
95
96
|
params = { profile: { name: 'John', surname: 'Cena' } }
|
96
|
-
cloned = UserCloner.call(user, traits: trait, **params)
|
97
|
+
cloned = UserCloner.call(user, traits: trait, **params).to_record
|
97
98
|
cloned.profile.jsonb_field
|
98
99
|
end
|
99
100
|
|
100
101
|
get_profile_jsonb(user, :default)
|
101
102
|
# => {}
|
102
103
|
|
103
|
-
get_profile_jsonb(user, :
|
104
|
+
get_profile_jsonb(user, :all_params)
|
104
105
|
# => { profile: { name: 'John', surname: 'Cena' } }
|
105
106
|
|
106
107
|
get_profile_jsonb(user, :by_key)
|
data/docs/sequel.md
CHANGED
@@ -3,9 +3,7 @@ id: sequel
|
|
3
3
|
title: Sequel
|
4
4
|
---
|
5
5
|
|
6
|
-
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.
|
7
|
-
|
8
|
-
Also, Sequel target record wrapped into a special class for implementation full Clowne's behavior. You need to use method `to_model` for getting final cloned `Sequel::Model` object (or you can use `save` for saving the cloned object to DB).
|
6
|
+
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.
|
9
7
|
|
10
8
|
Example:
|
11
9
|
|
@@ -29,13 +27,11 @@ and get cloned user
|
|
29
27
|
|
30
28
|
```ruby
|
31
29
|
user = User.last
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# => User
|
38
|
-
cloned_record.new?
|
30
|
+
operation = UserCloner.call(user)
|
31
|
+
# => <#Clowne::Adapters::Sequel::Operation...>
|
32
|
+
cloned = operation.to_record
|
33
|
+
# => <#User id: nil, ...>
|
34
|
+
cloned.new?
|
39
35
|
# => true
|
40
36
|
```
|
41
37
|
|
@@ -43,14 +39,15 @@ or you can save it immediately
|
|
43
39
|
|
44
40
|
```ruby
|
45
41
|
user = User.last
|
46
|
-
|
47
|
-
|
48
|
-
# => Clowne::Adapters::Sequel::
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
42
|
+
# => <#User id: 1, ...>
|
43
|
+
operation = UserCloner.call(user)
|
44
|
+
# => <#Clowne::Adapters::Sequel::Operation...>
|
45
|
+
operation.persist
|
46
|
+
# => true
|
47
|
+
cloned = operation.to_record
|
48
|
+
# => <#User id: 2, ...>
|
49
|
+
cloned.new?
|
53
50
|
# => false
|
54
51
|
```
|
55
52
|
|
56
|
-
If you try to clone
|
53
|
+
If you try to clone association without `NestedAttributes` plugin, Clowne will skip this declaration.
|
data/docs/supported_adapters.md
CHANGED
@@ -7,7 +7,7 @@ Clowne supports the following ORM adapters (and associations):
|
|
7
7
|
|
8
8
|
Adapter |1:1 | 1:M | M:M |
|
9
9
|
---------------------------------------------------|------------|-------------|-------------------------|
|
10
|
-
[:active_record](/
|
11
|
-
[:sequel](/
|
10
|
+
[:active_record](/docs/active_record.html) | has_one | has_many | has_and_belongs_to_many |
|
11
|
+
[:sequel](/docs/sequel.html) | one_to_one | one_to_many | many_to_many |
|
12
12
|
|
13
13
|
For more information see the corresponding adapter documentation.
|
data/docs/testing.md
CHANGED
@@ -140,7 +140,7 @@ end
|
|
140
140
|
Clowne automaticaly marks all specs in `spec/cloners` folder with `type: :cloner`. Otherwise you have to add this tag you.
|
141
141
|
|
142
142
|
|
143
|
-
##
|
143
|
+
## Using partial cloning
|
144
144
|
|
145
145
|
Under the hood, Clowne builds a [compilation plan](architecture.md) which is used to clone the record.
|
146
146
|
|
@@ -155,14 +155,14 @@ RSpec.describe UserCloner, type: :cloner do
|
|
155
155
|
|
156
156
|
specify 'simple case' do
|
157
157
|
# apply only the specified part of the plan
|
158
|
-
cloned_user = described_class.partial_apply(:nullify, user)
|
158
|
+
cloned_user = described_class.partial_apply(:nullify, user).to_record
|
159
159
|
expect(cloned_user.email).to be_nil
|
160
160
|
# finalize wasn't applied
|
161
161
|
expect(cloned_user.name).to eq 'Bombon'
|
162
162
|
end
|
163
163
|
|
164
164
|
specify 'with params' do
|
165
|
-
cloned_user = described_class.partial_apply(:finalize, user, name: 'new name')
|
165
|
+
cloned_user = described_class.partial_apply(:finalize, user, name: 'new name').to_record
|
166
166
|
# nullify actions were not applied!
|
167
167
|
expect(cloned_user.email).to eq user.email
|
168
168
|
# finalize was applied
|
@@ -171,7 +171,9 @@ RSpec.describe UserCloner, type: :cloner do
|
|
171
171
|
|
172
172
|
specify 'with traits' do
|
173
173
|
a_user = create(:user, name: 'Dindon')
|
174
|
-
cloned_user = described_class.partial_apply(
|
174
|
+
cloned_user = described_class.partial_apply(
|
175
|
+
:init_as, user, traits: :copy, target: a_user
|
176
|
+
).to_record
|
175
177
|
# returned user is the same as target
|
176
178
|
expect(cloned_user).to be_eql(a_user)
|
177
179
|
expect(cloned_user.name).to eq 'Bombon'
|
@@ -186,7 +188,7 @@ RSpec.describe UserCloner, type: :cloner do
|
|
186
188
|
# plan.apply(:association)
|
187
189
|
cloned_user = described_class.partial_apply(
|
188
190
|
'association.posts', user, traits: :with_popular_posts, min_rating: 1
|
189
|
-
)
|
191
|
+
).to_record
|
190
192
|
|
191
193
|
expect(cloned_user.posts.size).to eq 1
|
192
194
|
expect(cloned_user.posts.first.text).to eq 'Flying Dumplings'
|
data/docs/web/README.md
ADDED
data/docs/web/core/Footer.js
CHANGED
@@ -50,17 +50,13 @@ class Footer extends React.Component {
|
|
50
50
|
</a>
|
51
51
|
</div>
|
52
52
|
</div>
|
53
|
+
*/}
|
53
54
|
<div className="footer--block">
|
54
55
|
<h5>Resources</h5>
|
55
56
|
<div className="footer--list--item">
|
56
|
-
<a href="
|
57
|
+
<a href="https://evilmartians.com/chronicles/clowne-clone-ruby-models-with-a-smile" target="_blank">Clowne: clone Ruby models with a smile</a>
|
57
58
|
</div>
|
58
|
-
|
59
|
-
<a href="#" target="_blank">
|
60
|
-
Talk Slides
|
61
|
-
</a>
|
62
|
-
</div>
|
63
|
-
</div> */}
|
59
|
+
</div>
|
64
60
|
<div className="footer--block legals">
|
65
61
|
<p className="footer--copy">
|
66
62
|
<span className="copy">{yearLabel}</span>
|
@@ -77,10 +73,10 @@ class Footer extends React.Component {
|
|
77
73
|
<div className="footer--humanoids">
|
78
74
|
<div className="humanoids">
|
79
75
|
<div className="humanoids__martian">
|
80
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#
|
76
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#E2CBB5" d="M26 88L8 78l18-10"></path><path fill="#9FA628" d="M94 92v-6H44c-5.5 0-10-4.5-10-10s4.5-10 10-10h50V32c0-14-7.9-22-22-22H48c-14.1 0-22 8-22 22v60h68z"></path><circle fill="#FFF" cx="48" cy="50" r="8"></circle><circle fill="#FFF" cx="72" cy="50" r="8"></circle><circle fill="#BF6C35" cx="48" cy="50" r="4"></circle><circle fill="#BF6C35" cx="72" cy="50" r="4"></circle><g fill="#663F4C"><path d="M48 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM82 50c0 5.5-4.5 10-10 10s-10-4.5-10-10 4.5-10 10-10 10 4.5 10 10zm-16 0c0 3.3 2.7 6 6 6s6-2.7 6-6-2.7-6-6-6-6 2.7-6 6z"></path><path d="M102 8c-3.8 0-6-2.2-6-6 0-1.1-.9-2-2-2s-2 .9-2 2c0 2.2.5 4.1 1.5 5.7L88.2 13c-4-3.3-9.5-5-16.2-5H48c-6.7 0-12.2 1.7-16.2 4.9l-5.3-5.3C27.5 6.1 28 4.2 28 2c0-1.1-.9-2-2-2s-2 .9-2 2c0 3.8-2.2 6-6 6-1.1 0-2 .9-2 2s.9 2 2 2c2.2 0 4.1-.5 5.7-1.5l5.3 5.3c-3.2 4-4.9 9.5-4.9 16.2v34.8L3.9 78 24 89v3h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H44c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8h-2l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-4 8h-4l-4-8-3.1 6.2C37.1 80.7 36 78.5 36 76c0-4.4 3.6-8 8-8l4 8 4-8h4l4 8 4-8h4l4 8 4-8h4l4 8 4-8h8V32c0-6.7-1.7-12.2-4.9-16.2l5.3-5.3c1.6 1 3.5 1.5 5.7 1.5 1.1 0 2-.9 2-2s-1-2-2.1-2zM24 84.4L12.1 78 24 71.4v13z"></path></g></svg>
|
81
77
|
</div>
|
82
78
|
<div className="humanoids__human">
|
83
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#
|
79
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 108 92"><path fill="#E2CBB5" d="M14 62v30h68v-6H32c-5.5 0-10-4.5-10-10s4.5-10 10-10h50v-4c4 0 8-3.6 8-8s-4-8-8-8V32c0-14-7.9-22-22-22H36c-14.1 0-22 8-22 22v14c-4 0-8 3.6-8 8s4 8 8 8z"></path><circle fill="#FFF" cx="36" cy="50" r="8"></circle><circle fill="#FFF" cx="60" cy="50" r="8"></circle><circle fill="#9FA628" cx="36" cy="50" r="4"></circle><circle fill="#9FA628" cx="60" cy="50" r="4"></circle><path fill="#BF6C35" d="M60 10H36c-14.1 0-22 8-22 22v2l4-4 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 6 6 6-6 4 4v-2c0-14-7.9-22-22-22z"></path><g fill="#663F4C"><path d="M36 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6zM60 60c-5.5 0-10-4.5-10-10s4.5-10 10-10 10 4.5 10 10-4.5 10-10 10zm0-16c-3.3 0-6 2.7-6 6s2.7 6 6 6 6-2.7 6-6-2.7-6-6-6z"></path><path d="M12 63.8V92h4V32c0-12.9 7.1-20 20-20h24c12.9 0 20 7.1 20 20v32H32c-6.6 0-12 5.4-12 12s5.4 12 12 12h48v4h4v-8H74v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1h-6v-1c0-1.7-1.3-3-3-3s-3 1.3-3 3v1c-4 0-8-3.6-8-8s3.6-8 8-8h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h6v1c0 1.7 1.3 3 3 3s3-1.3 3-3v-1h4v-4.2c5-.9 8-5 8-9.8s-3-8.9-8-9.8V32c0-15.3-8.7-24-24-24H36c-15.3 0-24 8.7-24 24v12.2c-5 .9-8 5-8 9.8s3 8.9 8 9.8zm72-15.5c2 .8 4 3 4 5.7s-2 4.8-4 5.7V48.3zm-72 0v11.3c-2-.8-4-3-4-5.7s2-4.7 4-5.6z"></path></g></svg>
|
84
80
|
</div>
|
85
81
|
</div>
|
86
82
|
</div>
|
data/docs/web/i18n/en.json
CHANGED
@@ -5,23 +5,26 @@
|
|
5
5
|
"previous": "Previous",
|
6
6
|
"tagline": "A flexible gem for cloning your models",
|
7
7
|
"active_record": "Active Record",
|
8
|
+
"after_persist": "After Persist",
|
8
9
|
"alternatives": "Motivation & Alternatives",
|
9
10
|
"architecture": "Architecture",
|
10
11
|
"basic_example": "Basic Example",
|
11
|
-
"
|
12
|
+
"clone_mapper": "Clone mapper",
|
12
13
|
"customization": "Customization",
|
13
14
|
"exclude_association": "Exclude Association",
|
14
|
-
"execution_order": "Execution Order",
|
15
15
|
"finalize": "Finalization",
|
16
16
|
"Finalize": "Finalize",
|
17
|
+
"from_v02_to_v10": "From v0.2.x to v1.0.0",
|
17
18
|
"implicit_cloner": "Implicit Cloner",
|
18
19
|
"include_association": "Include Association",
|
19
20
|
"init_as": "Initialize Cloning Target",
|
20
21
|
"Init As": "Init As",
|
21
22
|
"inline_configuration": "Inline Configuration",
|
22
|
-
"installation": "Installation",
|
23
|
+
"installation": "Installation & Configuration",
|
23
24
|
"nullify": "Nullify Attributes",
|
24
25
|
"Nullify": "Nullify",
|
26
|
+
"operation": "Operation",
|
27
|
+
"overview": "Overview",
|
25
28
|
"parameters": "Parameters",
|
26
29
|
"sequel": "Sequel",
|
27
30
|
"supported_adapters": "Supported Adapters",
|
@@ -126,7 +129,8 @@
|
|
126
129
|
"Getting Started": "Getting Started",
|
127
130
|
"API": "API",
|
128
131
|
"Adapters": "Adapters",
|
129
|
-
"
|
132
|
+
"Advanced Options": "Advanced Options",
|
133
|
+
"Upgrade Notes": "Upgrade Notes"
|
130
134
|
},
|
131
135
|
"pages-strings": {
|
132
136
|
"Help Translate|recruit community translators for your project": "Help Translate",
|