clowne 0.1.0.beta1 → 0.1.0.pre1
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 +0 -4
- data/CHANGELOG.md +1 -3
- data/README.md +10 -100
- data/lib/clowne/adapters/active_record/associations/has_one.rb +1 -8
- data/lib/clowne/adapters/active_record/associations/noop.rb +1 -4
- data/lib/clowne/cloner.rb +4 -6
- data/lib/clowne/declarations/exclude_association.rb +5 -0
- data/lib/clowne/declarations/include_association.rb +2 -0
- data/lib/clowne/planner.rb +4 -11
- data/lib/clowne/version.rb +1 -1
- metadata +2 -3
- data/lib/clowne/adapters/active_record/dsl.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b833f076ba69c9b626d4038e977e4a40d8de8dbb
|
4
|
+
data.tar.gz: 271303497d931c2773c8c4f948baacfa9819cd4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4df02c8872ecdc0b1f70fd74a12549065aaf85cd6922b2648043905f3eba9d4bf415716a11b5f0a8f458d7c59d11edbe590e2e1c92f3328887ab9da38c7de1b
|
7
|
+
data.tar.gz: '0683a5756b9797ac8c2a9cd60a4adbd29c214b9dbd55ef3fd736e2e05b414a7b0d92493edcde7e9a109434ceb67ca557cc55953ec08f1caad2a2c35e13ac1439'
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
[](https://badge.fury.io/rb/clowne)
|
2
2
|
[](https://travis-ci.org/palkan/clowne)
|
3
|
+
[](https://codeclimate.com/github/palkan/clowne)
|
3
4
|
[](https://codeclimate.com/github/palkan/clowne/coverage)
|
4
5
|
|
5
6
|
# Clowne
|
6
7
|
|
7
|
-
**
|
8
|
+
**NOTICE**: gem is currently under heavy development, we plan to release the first version 'till the end of the year.
|
8
9
|
|
9
10
|
A flexible gem for cloning your models. Clowne focuses on ease of use and provides the ability to connect various ORM adapters (currently only ActiveRecord is supported).
|
10
11
|
|
@@ -64,7 +65,7 @@ class UserCloner < Clowne::Cloner
|
|
64
65
|
include_association :posts
|
65
66
|
|
66
67
|
nullify :login
|
67
|
-
|
68
|
+
|
68
69
|
# params here is an arbitrary hash passed into cloner
|
69
70
|
finalize do |_source, record, params|
|
70
71
|
record.email = params[:email]
|
@@ -81,38 +82,33 @@ end
|
|
81
82
|
and call it
|
82
83
|
|
83
84
|
```ruby
|
84
|
-
|
85
|
-
|
85
|
+
clone = UserCloner.call(User.last, { email: "fake@example.com" })
|
86
|
+
clone.persisted?
|
86
87
|
# => false
|
87
|
-
|
88
|
-
|
88
|
+
clone.save!
|
89
|
+
clone.login
|
89
90
|
# => nil
|
90
|
-
|
91
|
+
clone.email
|
91
92
|
# => "fake@example.com"
|
92
93
|
|
93
94
|
# associations:
|
94
|
-
|
95
|
+
clone.posts.count == User.last.posts.count
|
95
96
|
# => true
|
96
|
-
|
97
|
+
clone.profile.name
|
97
98
|
# => nil
|
98
99
|
```
|
99
100
|
|
100
101
|
## <a name="features">Features
|
101
102
|
|
102
103
|
- [Configuration](#configuration)
|
103
|
-
- [Include association](#include_association)
|
104
|
-
- - [Inline configuration](#config-inline)
|
105
104
|
- [Include one association](#include_association)
|
106
105
|
- - [Scope](#include_association_scope)
|
107
106
|
- - [Options](#include_association_options)
|
108
|
-
- - [Multiple associations](#include_associations)
|
109
107
|
- [Exclude association](#exclude_association)
|
110
|
-
- - [Multiple associations](#exclude_associations)
|
111
108
|
- [Nullify attribute(s)](#nullify)
|
112
109
|
- [Execute finalize block](#finalize)
|
113
110
|
- [Traits](#traits)
|
114
111
|
- [Execution order](#execution_order)
|
115
|
-
- [ActiveRecord DSL](#ar_dsl)
|
116
112
|
- [Customization](#customization)
|
117
113
|
|
118
114
|
### <a name="configuration"></a>Configuration
|
@@ -124,39 +120,6 @@ You can configure the default adapter for cloners:
|
|
124
120
|
Clowne.default_adapter = :active_record
|
125
121
|
```
|
126
122
|
|
127
|
-
#### <a name="config-inline"></a>Inline Configuration
|
128
|
-
|
129
|
-
You can also enhance the cloner configuration inline (i.e. add dynamic declarations):
|
130
|
-
|
131
|
-
```ruby
|
132
|
-
cloned = UserCloner.call(User.last) do
|
133
|
-
exclude_association :profile
|
134
|
-
|
135
|
-
finalize do |source, record|
|
136
|
-
record.email = "clone_of_#{source.email}"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
cloned.email
|
141
|
-
# => "clone_of_john@example.com"
|
142
|
-
|
143
|
-
# associations:
|
144
|
-
cloned.posts.size == User.last.posts.size
|
145
|
-
# => true
|
146
|
-
cloned.profile
|
147
|
-
# => nil
|
148
|
-
```
|
149
|
-
|
150
|
-
Inline enhancement doesn't affect the _global_ configuration, so you can use it without any fear.
|
151
|
-
|
152
|
-
Thus it's also possible to clone objects without any cloner classes at all by using `Clowne::Cloner`:
|
153
|
-
|
154
|
-
```ruby
|
155
|
-
cloned = Clowne::Cloner.call(user) do
|
156
|
-
# anything you want!
|
157
|
-
end
|
158
|
-
```
|
159
|
-
|
160
123
|
### <a name="include_association"></a>Include one association
|
161
124
|
|
162
125
|
Powerful declaration for including model's association.
|
@@ -260,23 +223,6 @@ UserCloner.call(user)
|
|
260
223
|
|
261
224
|
**Notice: if custom cloner is not defined, clowne tries to find default cloner and use it. (PostCloner for previous example)**
|
262
225
|
|
263
|
-
#### <a name="include_associations"></a>Include multiple association
|
264
|
-
|
265
|
-
It's possible to include multiple associations at once with default options and scope
|
266
|
-
|
267
|
-
```ruby
|
268
|
-
class User < ActiveRecord::Base
|
269
|
-
has_many :accounts
|
270
|
-
has_many :posts
|
271
|
-
end
|
272
|
-
|
273
|
-
class UserCloner < Clowne::Cloner
|
274
|
-
adapter :active_record
|
275
|
-
|
276
|
-
include_associations :accounts, :posts
|
277
|
-
end
|
278
|
-
```
|
279
|
-
|
280
226
|
### <a name="exclude_association"></a>Exclude association
|
281
227
|
|
282
228
|
Exclude association from copying
|
@@ -322,10 +268,6 @@ clone.comments.empty? #=> true
|
|
322
268
|
Why so? That allows to have deterministic cloning plans when combining multiple traits
|
323
269
|
(or inheriting cloners).
|
324
270
|
|
325
|
-
#### <a name="exclude_associations"></a>Exclude multiple association
|
326
|
-
|
327
|
-
It's possible to exclude multiple associations the same way as `include_associations` but with `exclude_associations`
|
328
|
-
|
329
271
|
### <a name="nullify"></a>Nullify attribute(s)
|
330
272
|
|
331
273
|
Nullify attributes:
|
@@ -437,38 +379,6 @@ For ActiveRecord:
|
|
437
379
|
- run `finalize` blocks
|
438
380
|
The order of `finalize` blocks is the order they've been written.
|
439
381
|
|
440
|
-
### <a name="ar_dsl"></a>Active Record DSL
|
441
|
-
|
442
|
-
Clowne provides an optional ActiveRecord integration which allows you to configure cloners in your models and adds a shortcut to invoke cloners (`#clowne` method). (Note: that's exactly the way [`amoeba`](https://github.com/amoeba-rb/amoeba) works).
|
443
|
-
|
444
|
-
To enable this integration you must require `"clowne/adapters/active_record/dsl"` somewhere in your app, e.g. in initializer:
|
445
|
-
|
446
|
-
```ruby
|
447
|
-
# config/initializers/clowne.rb
|
448
|
-
require "clowne/adapters/active_record/dsl"
|
449
|
-
```
|
450
|
-
|
451
|
-
Now you can specify cloning configs in your AR models:
|
452
|
-
|
453
|
-
```ruby
|
454
|
-
class User < ActiveRecord::Base
|
455
|
-
clowne_config do
|
456
|
-
include_associations :profile
|
457
|
-
|
458
|
-
nullify :email
|
459
|
-
|
460
|
-
# whatever available for your cloners,
|
461
|
-
# active_record adapter is set implicitly here
|
462
|
-
end
|
463
|
-
end
|
464
|
-
```
|
465
|
-
|
466
|
-
And then you can clone objects like this:
|
467
|
-
|
468
|
-
```ruby
|
469
|
-
cloned_user = user.clowne(traits: my_traits, **params)
|
470
|
-
```
|
471
|
-
|
472
382
|
### <a name="customization"></a>Customization
|
473
383
|
|
474
384
|
Clowne is built with extensibility in mind. You can create your own DSL commands and resolvers.
|
@@ -5,16 +5,10 @@ module Clowne
|
|
5
5
|
class ActiveRecord
|
6
6
|
module Associations
|
7
7
|
class HasOne < Base
|
8
|
-
# rubocop: disable Metrics/MethodLength
|
9
8
|
def call(record)
|
10
9
|
child = association
|
11
10
|
return record unless child
|
12
|
-
unless scope.nil?
|
13
|
-
warn(
|
14
|
-
'[Clowne] Has one association does not support scopes ' \
|
15
|
-
"(#{@association_name} for #{@source.class})"
|
16
|
-
)
|
17
|
-
end
|
11
|
+
warn '[Clowne] Has one association does not support scopes' unless scope.nil?
|
18
12
|
|
19
13
|
child_clone = clone_one(child)
|
20
14
|
child_clone[:"#{reflection.foreign_key}"] = nil
|
@@ -22,7 +16,6 @@ module Clowne
|
|
22
16
|
|
23
17
|
record
|
24
18
|
end
|
25
|
-
# rubocop: enable Metrics/MethodLength
|
26
19
|
end
|
27
20
|
end
|
28
21
|
end
|
@@ -6,10 +6,7 @@ module Clowne
|
|
6
6
|
module Associations
|
7
7
|
class Noop < Base
|
8
8
|
def call(record)
|
9
|
-
warn(
|
10
|
-
"[Clowne] Reflection #{reflection.class.name} is not supported "\
|
11
|
-
"(#{@association_name} for #{@source.class})"
|
12
|
-
)
|
9
|
+
warn("[Clowne] Reflection #{reflection.class.name} is not supported")
|
13
10
|
record
|
14
11
|
end
|
15
12
|
end
|
data/lib/clowne/cloner.rb
CHANGED
@@ -38,8 +38,8 @@ module Clowne # :nodoc: all
|
|
38
38
|
@traits[name].extend_with(block)
|
39
39
|
end
|
40
40
|
|
41
|
-
# rubocop: disable Metrics/AbcSize
|
42
|
-
# rubocop: disable Metrics/
|
41
|
+
# rubocop: disable Metrics/AbcSize
|
42
|
+
# rubocop: disable Metrics/MethodLength
|
43
43
|
def call(object, **options)
|
44
44
|
raise(UnprocessableSourceError, 'Nil is not cloneable object') if object.nil?
|
45
45
|
|
@@ -56,13 +56,11 @@ module Clowne # :nodoc: all
|
|
56
56
|
plan_with_traits(traits)
|
57
57
|
end
|
58
58
|
|
59
|
-
plan = Clowne::Planner.enhance(plan, Proc.new) if block_given?
|
60
|
-
|
61
59
|
adapter.clone(object, plan, params: options)
|
62
60
|
end
|
63
61
|
|
64
|
-
# rubocop: enable Metrics/AbcSize
|
65
|
-
# rubocop: enable Metrics/
|
62
|
+
# rubocop: enable Metrics/AbcSize
|
63
|
+
# rubocop: enable Metrics/MethodLength
|
66
64
|
|
67
65
|
def default_plan
|
68
66
|
return @default_plan if instance_variable_defined?(:@default_plan)
|
@@ -11,6 +11,11 @@ module Clowne
|
|
11
11
|
|
12
12
|
def compile(plan)
|
13
13
|
plan.remove_from(:association, name)
|
14
|
+
|
15
|
+
# update all_associations plan
|
16
|
+
all_associations = plan.get(:all_associations)
|
17
|
+
return if all_associations.nil?
|
18
|
+
all_associations.except! name
|
14
19
|
end
|
15
20
|
end
|
16
21
|
end
|
data/lib/clowne/planner.rb
CHANGED
@@ -5,7 +5,10 @@ require 'clowne/plan'
|
|
5
5
|
module Clowne
|
6
6
|
class Planner # :nodoc: all
|
7
7
|
class << self
|
8
|
-
#
|
8
|
+
# Params:
|
9
|
+
# +cloner+:: Cloner object
|
10
|
+
# +init_plan+:: Init plan
|
11
|
+
# +traits+:: List of traits if any
|
9
12
|
def compile(cloner, traits: nil)
|
10
13
|
declarations = cloner.declarations.dup
|
11
14
|
|
@@ -16,16 +19,6 @@ module Clowne
|
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
|
-
# Extend previously compiled plan with an arbitrary block
|
20
|
-
# NOTE: It doesn't modify the plan itself but return a copy
|
21
|
-
def enhance(plan, block)
|
22
|
-
trait = Clowne::Declarations::Trait.new.tap { |t| t.extend_with(block) }
|
23
|
-
|
24
|
-
trait.compiled.each_with_object(plan.dup) do |declaration, new_plan|
|
25
|
-
declaration.compile(new_plan)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
22
|
private
|
30
23
|
|
31
24
|
def compile_traits(cloner, traits)
|
data/lib/clowne/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clowne
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-01-
|
12
|
+
date: 2018-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -114,7 +114,6 @@ files:
|
|
114
114
|
- lib/clowne/adapters/active_record/associations/has_many.rb
|
115
115
|
- lib/clowne/adapters/active_record/associations/has_one.rb
|
116
116
|
- lib/clowne/adapters/active_record/associations/noop.rb
|
117
|
-
- lib/clowne/adapters/active_record/dsl.rb
|
118
117
|
- lib/clowne/adapters/base.rb
|
119
118
|
- lib/clowne/adapters/base/finalize.rb
|
120
119
|
- lib/clowne/adapters/base/nullify.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Clowne
|
4
|
-
module Adapters
|
5
|
-
# Extend ActiveRecord with Clowne DSL and methods
|
6
|
-
module ActiveRecordDSL
|
7
|
-
module InstanceMethods # :nodoc:
|
8
|
-
# Shortcut to call class's cloner call with self
|
9
|
-
def clowne(*args)
|
10
|
-
self.class.cloner_class.call(self, *args)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module ClassMethods # :nodoc:
|
15
|
-
def clowne_config(options = {}, &block)
|
16
|
-
if options.delete(:inherit) != false && superclass.respond_to?(:cloner_class)
|
17
|
-
parent_cloner = superclass.cloner_class
|
18
|
-
end
|
19
|
-
|
20
|
-
parent_cloner ||= Clowne::Cloner
|
21
|
-
cloner = instance_variable_set(:@_clowne_cloner, Class.new(parent_cloner))
|
22
|
-
cloner.adapter :active_record
|
23
|
-
cloner.instance_exec(&block)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
ActiveSupport.on_load(:active_record) do
|
31
|
-
::ActiveRecord::Base.extend Clowne::Adapters::ActiveRecordDSL::ClassMethods
|
32
|
-
::ActiveRecord::Base.include Clowne::Adapters::ActiveRecordDSL::InstanceMethods
|
33
|
-
end
|