clowne 1.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/README.md +1 -4
- data/lib/clowne/adapters/active_record/associations/belongs_to.rb +0 -1
- data/lib/clowne/adapters/active_record/associations/has_one.rb +0 -1
- data/lib/clowne/adapters/active_record/associations/noop.rb +1 -1
- data/lib/clowne/adapters/active_record/associations.rb +1 -1
- data/lib/clowne/adapters/active_record/resolvers/association.rb +0 -1
- data/lib/clowne/adapters/base/association.rb +1 -1
- data/lib/clowne/adapters/base.rb +2 -2
- data/lib/clowne/adapters/sequel/associations/one_to_many.rb +0 -4
- data/lib/clowne/adapters/sequel/associations.rb +1 -1
- data/lib/clowne/adapters/sequel/resolvers/association.rb +0 -1
- data/lib/clowne/cloner.rb +1 -1
- data/lib/clowne/declarations/trait.rb +1 -1
- data/lib/clowne/declarations.rb +1 -1
- data/lib/clowne/ext/record_key.rb +1 -1
- data/lib/clowne/resolvers/after_clone.rb +2 -1
- data/lib/clowne/resolvers/init_as.rb +0 -1
- data/lib/clowne/rspec/clone_association.rb +0 -1
- data/lib/clowne/version.rb +1 -1
- data/lib/clowne.rb +2 -3
- metadata +7 -110
- data/.codeclimate.yml +0 -7
- data/.gitattributes +0 -1
- data/.gitignore +0 -16
- data/.rspec +0 -3
- data/.rubocop.yml +0 -28
- data/.rufo +0 -3
- data/.travis.yml +0 -45
- data/Gemfile +0 -20
- data/Rakefile +0 -8
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/clowne.gemspec +0 -36
- data/docs/.nojekyll +0 -0
- data/docs/.rubocop.yml +0 -18
- data/docs/CNAME +0 -1
- data/docs/README.md +0 -131
- data/docs/_sidebar.md +0 -25
- data/docs/active_record.md +0 -33
- data/docs/after_clone.md +0 -53
- data/docs/after_persist.md +0 -77
- data/docs/architecture.md +0 -138
- data/docs/assets/docsify.min.js +0 -1
- data/docs/assets/prism-ruby.min.js +0 -1
- data/docs/assets/styles.css +0 -348
- data/docs/assets/vue.css +0 -1
- data/docs/clone_mapper.md +0 -59
- data/docs/customization.md +0 -63
- data/docs/exclude_association.md +0 -61
- data/docs/finalize.md +0 -31
- data/docs/from_v02_to_v1.md +0 -83
- data/docs/getting_started.md +0 -171
- data/docs/implicit_cloner.md +0 -33
- data/docs/include_association.md +0 -133
- data/docs/index.html +0 -29
- data/docs/init_as.md +0 -40
- data/docs/inline_configuration.md +0 -37
- data/docs/nullify.md +0 -33
- data/docs/operation.md +0 -55
- data/docs/parameters.md +0 -112
- data/docs/sequel.md +0 -50
- data/docs/supported_adapters.md +0 -10
- data/docs/testing.md +0 -194
- data/docs/traits.md +0 -25
- data/gemfiles/activerecord42.gemfile +0 -9
- data/gemfiles/jruby.gemfile +0 -10
- data/gemfiles/railsmaster.gemfile +0 -10
- data/lib/clowne/ext/yield_self_then.rb +0 -25
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.
|
data/docs/supported_adapters.md
DELETED
@@ -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.
|
data/docs/testing.md
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
# Testing
|
2
|
-
|
3
|
-
Clowne provides specific tools to help you test your cloners.
|
4
|
-
|
5
|
-
The main goal is to make it possible to test different cloning phases separately and avoid _heavy_ tests setup phases.
|
6
|
-
|
7
|
-
Let's consider the following models and cloners:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
# app/models/user.rb
|
11
|
-
class User < ApplicationRecord
|
12
|
-
has_one :profile
|
13
|
-
has_many :posts
|
14
|
-
end
|
15
|
-
|
16
|
-
# app/models/post.rb
|
17
|
-
class Post < ApplicationRecord
|
18
|
-
has_many :comments
|
19
|
-
has_many :votes
|
20
|
-
|
21
|
-
scope :draft, -> { where(draft: true) }
|
22
|
-
end
|
23
|
-
|
24
|
-
# app/cloners/user_cloner.rb
|
25
|
-
class UserCloner < Clowne::Cloner
|
26
|
-
class ProfileCloner
|
27
|
-
nullify :rating
|
28
|
-
end
|
29
|
-
|
30
|
-
include_association :profile, clone_with: ProfileCloner
|
31
|
-
|
32
|
-
nullify :email
|
33
|
-
|
34
|
-
finalize do |_, record, name: nil, **|
|
35
|
-
record.name = name unless name.nil?
|
36
|
-
end
|
37
|
-
|
38
|
-
trait :copy do
|
39
|
-
init_as do |user, target:, **|
|
40
|
-
# copy name
|
41
|
-
target.name = user.name
|
42
|
-
target
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
trait :with_posts do
|
47
|
-
include_association :posts, :draft, traits: :mark_as_copy
|
48
|
-
end
|
49
|
-
|
50
|
-
trait :with_popular_posts do
|
51
|
-
include_association :posts, (lambda do |params|
|
52
|
-
where("rating > ?", params[:min_rating])
|
53
|
-
end)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# app/cloners/post_cloner.rb
|
58
|
-
class PostCloner < Clowne::Cloner
|
59
|
-
include_association :comments
|
60
|
-
|
61
|
-
trait :mark_as_copy do |_, record|
|
62
|
-
record.title += " (copy)"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
```
|
66
|
-
|
67
|
-
## Getting started
|
68
|
-
|
69
|
-
Currently, only [RSpec](http://rspec.info/) is supported.
|
70
|
-
|
71
|
-
Add this line to your `spec_helper.rb` (or `rails_helper.rb`):
|
72
|
-
|
73
|
-
```ruby
|
74
|
-
require "clowne/rspec"
|
75
|
-
```
|
76
|
-
|
77
|
-
## Configuration matchers
|
78
|
-
|
79
|
-
There are several matchers that allow you to verify the cloner configuration.
|
80
|
-
|
81
|
-
### `clone_associations`
|
82
|
-
|
83
|
-
This matcher vefifies that your cloner includes the specified associations:
|
84
|
-
|
85
|
-
```ruby
|
86
|
-
# spec/cloners/user_cloner_spec.rb
|
87
|
-
RSpec.describe UserCloner, type: :cloner do
|
88
|
-
subject { described_class }
|
89
|
-
|
90
|
-
specify do
|
91
|
-
# checks that only the specified associations is included
|
92
|
-
is_expected.to clone_associations(:profile)
|
93
|
-
|
94
|
-
# with traits
|
95
|
-
is_expected.to clone_associations(:profile, :posts)
|
96
|
-
.with_traits(:with_posts)
|
97
|
-
|
98
|
-
# raises when there are some unspecified associations
|
99
|
-
is_expected.to clone_associations(:profile)
|
100
|
-
.with_traits(:with_posts)
|
101
|
-
#=> raises RSpec::Expectations::ExpectationNotMetError
|
102
|
-
end
|
103
|
-
end
|
104
|
-
```
|
105
|
-
|
106
|
-
### `clone_association`
|
107
|
-
|
108
|
-
This matcher allows to verify the specified association options:
|
109
|
-
|
110
|
-
```ruby
|
111
|
-
# spec/cloners/user_cloner_spec.rb
|
112
|
-
RSpec.describe UserCloner, type: :cloner do
|
113
|
-
subject { described_class }
|
114
|
-
|
115
|
-
specify do
|
116
|
-
# simply check that association is included
|
117
|
-
is_expected.to clone_association(:profile)
|
118
|
-
|
119
|
-
# check options
|
120
|
-
is_expected.to clone_association(
|
121
|
-
:profile,
|
122
|
-
clone_with: described_class::ProfileCloner
|
123
|
-
)
|
124
|
-
|
125
|
-
# with traits, scope and activated trait
|
126
|
-
is_expected.to clone_association(
|
127
|
-
:posts,
|
128
|
-
traits: :mark_as_copy,
|
129
|
-
scope: :draft
|
130
|
-
).with_traits(:with_posts)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
```
|
134
|
-
|
135
|
-
**NOTE:** `clone_associations`/`clone_association` matchers are only available in groups marked with `type: :cloner` tag.
|
136
|
-
|
137
|
-
Clowne automaticaly marks all specs in `spec/cloners` folder with `type: :cloner`. Otherwise you have to add this tag you.
|
138
|
-
|
139
|
-
|
140
|
-
## Using partial cloning
|
141
|
-
|
142
|
-
Under the hood, Clowne builds a [compilation plan](architecture.md) which is used to clone the record.
|
143
|
-
|
144
|
-
Plan is a set of _actions_ (such as `nullify`, `finalize`, `association`, `init_as`) which are applied to the record.
|
145
|
-
|
146
|
-
Most of the time these actions don't depend on each other, thus we can test them separately:
|
147
|
-
|
148
|
-
```ruby
|
149
|
-
# spec/cloners/user_cloner_spec.rb
|
150
|
-
RSpec.describe UserCloner, type: :cloner do
|
151
|
-
subject(:user) { create :user, name: "Bombon" }
|
152
|
-
|
153
|
-
specify "simple case" do
|
154
|
-
# apply only the specified part of the plan
|
155
|
-
cloned_user = described_class.partial_apply(:nullify, user).to_record
|
156
|
-
expect(cloned_user.email).to be_nil
|
157
|
-
# finalize wasn't applied
|
158
|
-
expect(cloned_user.name).to eq "Bombon"
|
159
|
-
end
|
160
|
-
|
161
|
-
specify "with params" do
|
162
|
-
cloned_user = described_class.partial_apply(:finalize, user, name: "new name").to_record
|
163
|
-
# nullify actions were not applied!
|
164
|
-
expect(cloned_user.email).to eq user.email
|
165
|
-
# finalize was applied
|
166
|
-
expect(cloned_user.name).to eq "new name"
|
167
|
-
end
|
168
|
-
|
169
|
-
specify "with traits" do
|
170
|
-
a_user = create(:user, name: "Dindon")
|
171
|
-
cloned_user = described_class.partial_apply(
|
172
|
-
:init_as, user, traits: :copy, target: a_user
|
173
|
-
).to_record
|
174
|
-
# returned user is the same as target
|
175
|
-
expect(cloned_user).to be_eql(a_user)
|
176
|
-
expect(cloned_user.name).to eq "Bombon"
|
177
|
-
end
|
178
|
-
|
179
|
-
specify "associations" do
|
180
|
-
create(:post, user: user, rating: 1, text: "Boom Boom")
|
181
|
-
create(:post, user: user, rating: 2, text: "Flying Dumplings")
|
182
|
-
|
183
|
-
# you can specify which associations to include (you can use array)
|
184
|
-
# to apply all associations write:
|
185
|
-
# plan.apply(:association)
|
186
|
-
cloned_user = described_class.partial_apply(
|
187
|
-
"association.posts", user, traits: :with_popular_posts, min_rating: 1
|
188
|
-
).to_record
|
189
|
-
|
190
|
-
expect(cloned_user.posts.size).to eq 1
|
191
|
-
expect(cloned_user.posts.first.text).to eq "Flying Dumplings"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
```
|
data/docs/traits.md
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# Traits
|
2
|
-
|
3
|
-
Traits allow you to group cloner declarations together and then apply them (like in [`factory_bot`](https://github.com/thoughtbot/factory_bot)):
|
4
|
-
|
5
|
-
```ruby
|
6
|
-
class UserCloner < Clowne::Cloner
|
7
|
-
trait :with_posts do
|
8
|
-
include_association :posts
|
9
|
-
end
|
10
|
-
|
11
|
-
trait :with_profile do
|
12
|
-
include_association :profile
|
13
|
-
end
|
14
|
-
|
15
|
-
trait :nullify_name do
|
16
|
-
nullify :name
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
UserCloner.call(user, traits: %i[with_posts with_profile nullify_name])
|
21
|
-
# or
|
22
|
-
UserCloner.call(user, traits: :nullify_name)
|
23
|
-
# or
|
24
|
-
# ...
|
25
|
-
```
|
data/gemfiles/jruby.gemfile
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Clowne
|
4
|
-
module Ext # :nodoc: all
|
5
|
-
# Add yield_self and then if missing
|
6
|
-
module YieldSelfThen
|
7
|
-
module Ext
|
8
|
-
unless nil.respond_to?(:yield_self)
|
9
|
-
def yield_self
|
10
|
-
yield self
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
alias then yield_self
|
15
|
-
end
|
16
|
-
|
17
|
-
# See https://github.com/jruby/jruby/issues/5220
|
18
|
-
::Object.include(Ext) if RUBY_PLATFORM.match?(/java/i)
|
19
|
-
|
20
|
-
refine Object do
|
21
|
-
include Ext
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|