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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/.travis.yml +6 -3
  4. data/CHANGELOG.md +14 -0
  5. data/Gemfile +1 -1
  6. data/README.md +30 -9
  7. data/clowne.gemspec +4 -3
  8. data/docs/active_record.md +2 -2
  9. data/docs/after_persist.md +80 -0
  10. data/docs/basic_example.md +22 -5
  11. data/docs/clone_mapper.md +62 -0
  12. data/docs/customization.md +2 -1
  13. data/docs/exclude_association.md +5 -4
  14. data/docs/finalize.md +2 -2
  15. data/docs/from_v02_to_v1.md +91 -0
  16. data/docs/implicit_cloner.md +1 -1
  17. data/docs/include_association.md +4 -4
  18. data/docs/init_as.md +10 -2
  19. data/docs/inline_configuration.md +4 -2
  20. data/docs/installation.md +31 -1
  21. data/docs/nullify.md +2 -2
  22. data/docs/operation.md +58 -0
  23. data/docs/overview.md +24 -0
  24. data/docs/parameters.md +5 -4
  25. data/docs/sequel.md +15 -18
  26. data/docs/supported_adapters.md +2 -2
  27. data/docs/testing.md +7 -5
  28. data/docs/web/README.md +6 -0
  29. data/docs/web/core/Footer.js +5 -9
  30. data/docs/web/i18n/en.json +8 -4
  31. data/docs/web/pages/en/index.js +1 -1
  32. data/docs/web/sidebars.json +10 -4
  33. data/docs/web/siteConfig.js +6 -4
  34. data/docs/web/static/css/custom.css +16 -10
  35. data/gemfiles/activerecord42.gemfile +3 -1
  36. data/gemfiles/jruby.gemfile +2 -0
  37. data/gemfiles/railsmaster.gemfile +2 -0
  38. data/lib/clowne.rb +3 -0
  39. data/lib/clowne/adapters/active_record.rb +2 -3
  40. data/lib/clowne/adapters/active_record/associations/base.rb +0 -4
  41. data/lib/clowne/adapters/active_record/associations/has_one.rb +2 -1
  42. data/lib/clowne/adapters/active_record/resolvers/association.rb +38 -0
  43. data/lib/clowne/adapters/base.rb +42 -43
  44. data/lib/clowne/adapters/base/association.rb +24 -15
  45. data/lib/clowne/adapters/registry.rb +49 -0
  46. data/lib/clowne/adapters/sequel.rb +10 -6
  47. data/lib/clowne/adapters/sequel/associations/base.rb +8 -4
  48. data/lib/clowne/adapters/sequel/associations/many_to_many.rb +6 -2
  49. data/lib/clowne/adapters/sequel/associations/one_to_many.rb +7 -2
  50. data/lib/clowne/adapters/sequel/associations/one_to_one.rb +7 -2
  51. data/lib/clowne/adapters/sequel/operation.rb +32 -0
  52. data/lib/clowne/adapters/sequel/record_wrapper.rb +0 -16
  53. data/lib/clowne/adapters/sequel/resolvers/after_persist.rb +22 -0
  54. data/lib/clowne/adapters/sequel/resolvers/association.rb +51 -0
  55. data/lib/clowne/adapters/sequel/specifications/after_persist_does_not_support.rb +15 -0
  56. data/lib/clowne/cloner.rb +27 -20
  57. data/lib/clowne/declarations.rb +2 -1
  58. data/lib/clowne/declarations/after_persist.rb +21 -0
  59. data/lib/clowne/declarations/finalize.rb +1 -0
  60. data/lib/clowne/declarations/include_association.rb +2 -1
  61. data/lib/clowne/declarations/init_as.rb +1 -0
  62. data/lib/clowne/declarations/nullify.rb +1 -0
  63. data/lib/clowne/declarations/trait.rb +1 -0
  64. data/lib/clowne/dsl.rb +9 -0
  65. data/lib/clowne/ext/lambda_as_proc.rb +1 -0
  66. data/lib/clowne/ext/record_key.rb +12 -0
  67. data/lib/clowne/ext/yield_self_then.rb +25 -0
  68. data/lib/clowne/planner.rb +6 -3
  69. data/lib/clowne/resolvers/after_persist.rb +18 -0
  70. data/lib/clowne/resolvers/finalize.rb +12 -0
  71. data/lib/clowne/resolvers/init_as.rb +13 -0
  72. data/lib/clowne/resolvers/nullify.rb +15 -0
  73. data/lib/clowne/rspec/helpers.rb +1 -0
  74. data/lib/clowne/utils/clone_mapper.rb +26 -0
  75. data/lib/clowne/utils/operation.rb +83 -0
  76. data/lib/clowne/utils/options.rb +39 -0
  77. data/lib/clowne/utils/params.rb +64 -0
  78. data/lib/clowne/utils/plan.rb +90 -0
  79. data/lib/clowne/version.rb +1 -1
  80. metadata +44 -18
  81. data/docs/configuration.md +0 -29
  82. data/docs/execution_order.md +0 -14
  83. data/docs/web/static/fonts/StemText.woff +0 -0
  84. data/docs/web/static/fonts/StemTextBold.woff +0 -0
  85. data/lib/clowne/adapters/active_record/association.rb +0 -34
  86. data/lib/clowne/adapters/base/finalize.rb +0 -19
  87. data/lib/clowne/adapters/base/init_as.rb +0 -21
  88. data/lib/clowne/adapters/base/nullify.rb +0 -19
  89. data/lib/clowne/adapters/sequel/association.rb +0 -47
  90. data/lib/clowne/params.rb +0 -62
  91. data/lib/clowne/plan.rb +0 -83
@@ -28,7 +28,7 @@ user = User.last
28
28
  user.profile.name
29
29
  #=> "Bimbo"
30
30
 
31
- cloned = UserCloner.call(user)
31
+ cloned = UserCloner.call(user).to_record
32
32
  cloned.profile.name
33
33
  # => "Clone of Bimbo"
34
34
  ```
@@ -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).
@@ -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 settings to another user,
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
- cloned = UserCloner.call(User.last) do
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..
@@ -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).
@@ -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?
@@ -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
+ ```
@@ -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
@@ -16,7 +16,8 @@ class UserCloner < Clowne::Cloner
16
16
  end
17
17
  end
18
18
 
19
- cloned = UserCloner.call(user, state: :draft, email: 'cloned@example.com')
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 :params_true do
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, :params_true)
104
+ get_profile_jsonb(user, :all_params)
104
105
  # => { profile: { name: 'John', surname: 'Cena' } }
105
106
 
106
107
  get_profile_jsonb(user, :by_key)
@@ -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
- wrapper = UserCloner.call(user)
33
- wrapper.class
34
- # => Clowne::Adapters::Sequel::RecordWrapper
35
- cloned_record = wrapper.to_model
36
- cloned_record.class
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
- wrapper = UserCloner.call(user)
47
- wrapper.class
48
- # => Clowne::Adapters::Sequel::RecordWrapper
49
- cloned_record = wrapper.save
50
- cloned_record.class
51
- # => User
52
- cloned_record.new?
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 associations without `NestedAttributes` plugin, Clowne will skip this declaration.
53
+ If you try to clone association without `NestedAttributes` plugin, Clowne will skip this declaration.
@@ -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](/clowne/docs/active_record.html) | has_one | has_many | has_and_belongs_to_many |
11
- [:sequel](/clowne/docs/sequel.html) | one_to_one | one_to_many | many_to_many |
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.
@@ -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
- ## Clone actions matchers
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(:init_as, user, traits: :copy, target: a_user)
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'
@@ -0,0 +1,6 @@
1
+ # Clowne Docs
2
+
3
+ 1. Install `node` and `yarn`
4
+ 2. `$ yarn` - install frontend deps
5
+ 3. `$ yarn run start --port 3002`
6
+ 4. `$ open http://localhost:3002`
@@ -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="#" target="_blank">Super puper chronicles post</a>
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
- <div className="footer--list--item">
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>&nbsp;
@@ -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="#111" d="M26 88L8 78l18-10"/><path fill="#111" 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"/><circle fill="#FFF" cx="48" cy="50" r="8"/><circle fill="#FFF" cx="72" cy="50" r="8"/><circle fill="#363636" cx="48" cy="50" r="4"/><circle fill="#363636" cx="72" cy="50" r="4"/><g fill="#fff"><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 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"/></g></svg>
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="#FFF" 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"/><circle fill="#FFF" cx="36" cy="50" r="8"/><circle fill="#FFF" cx="60" cy="50" r="8"/><circle fill="#363636" cx="36" cy="50" r="4"/><circle fill="#363636" cx="60" cy="50" r="4"/><path fill="#363636" 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"/><g fill="#111"><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 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"/></g></svg>
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>
@@ -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
- "configuration": "Configuration",
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
- "Features": "Features"
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",