clowne 0.1.0.beta1 → 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/clowne.svg)](https://badge.fury.io/rb/clowne)
|
2
2
|
[![Build Status](https://travis-ci.org/palkan/clowne.svg?branch=master)](https://travis-ci.org/palkan/clowne)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/palkan/clowne.svg)](https://codeclimate.com/github/palkan/clowne)
|
3
4
|
[![Test Coverage](https://codeclimate.com/github/palkan/clowne/badges/coverage.svg)](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
|