clowne 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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",
|