clowne 0.1.0.pre1 → 0.1.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/.codeclimate.yml +7 -0
- data/.gitattributes +1 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +17 -0
- data/.travis.yml +15 -2
- data/CHANGELOG.md +9 -2
- data/Gemfile +1 -0
- data/README.md +25 -381
- data/clowne.gemspec +1 -0
- data/docs/.rubocop.yml +12 -0
- data/docs/active_record.md +36 -0
- data/docs/alternatives.md +26 -0
- data/docs/architecture.md +141 -0
- data/docs/basic_example.md +66 -0
- data/docs/configuration.md +29 -0
- data/docs/customization.md +64 -0
- data/docs/exclude_association.md +63 -0
- data/docs/execution_order.md +14 -0
- data/docs/finalize.md +35 -0
- data/docs/implicit_cloner.md +36 -0
- data/docs/include_association.md +119 -0
- data/docs/init_as.md +36 -0
- data/docs/inline_configuration.md +38 -0
- data/docs/installation.md +16 -0
- data/docs/nullify.md +37 -0
- data/docs/sequel.md +56 -0
- data/docs/supported_adapters.md +13 -0
- data/docs/traits.md +28 -0
- data/docs/web/.gitignore +11 -0
- data/docs/web/core/Footer.js +92 -0
- data/docs/web/i18n/en.json +134 -0
- data/docs/web/package.json +14 -0
- data/docs/web/pages/en/help.js +50 -0
- data/docs/web/pages/en/index.js +231 -0
- data/docs/web/pages/en/users.js +47 -0
- data/docs/web/sidebars.json +30 -0
- data/docs/web/siteConfig.js +44 -0
- data/docs/web/static/css/custom.css +229 -0
- data/docs/web/static/fonts/FiraCode-Medium.woff +0 -0
- data/docs/web/static/fonts/FiraCode-Regular.woff +0 -0
- data/docs/web/static/fonts/StemText.woff +0 -0
- data/docs/web/static/fonts/StemTextBold.woff +0 -0
- data/docs/web/static/img/favicon/favicon.ico +0 -0
- data/docs/web/yarn.lock +1741 -0
- data/gemfiles/activerecord42.gemfile +1 -0
- data/gemfiles/jruby.gemfile +2 -0
- data/gemfiles/railsmaster.gemfile +1 -0
- data/lib/clowne.rb +3 -1
- data/lib/clowne/adapters/active_record.rb +3 -12
- data/lib/clowne/adapters/active_record/association.rb +1 -1
- data/lib/clowne/adapters/active_record/associations/base.rb +8 -48
- data/lib/clowne/adapters/active_record/associations/has_one.rb +8 -1
- data/lib/clowne/adapters/active_record/associations/noop.rb +4 -1
- data/lib/clowne/adapters/active_record/dsl.rb +33 -0
- data/lib/clowne/adapters/base.rb +13 -6
- data/lib/clowne/adapters/base/association.rb +69 -0
- data/lib/clowne/adapters/base/finalize.rb +1 -1
- data/lib/clowne/adapters/base/init_as.rb +21 -0
- data/lib/clowne/adapters/registry.rb +5 -11
- data/lib/clowne/adapters/sequel.rb +25 -0
- data/lib/clowne/adapters/sequel/association.rb +47 -0
- data/lib/clowne/adapters/sequel/associations.rb +26 -0
- data/lib/clowne/adapters/sequel/associations/base.rb +23 -0
- data/lib/clowne/adapters/sequel/associations/many_to_many.rb +19 -0
- data/lib/clowne/adapters/sequel/associations/noop.rb +16 -0
- data/lib/clowne/adapters/sequel/associations/one_to_many.rb +23 -0
- data/lib/clowne/adapters/sequel/associations/one_to_one.rb +23 -0
- data/lib/clowne/adapters/sequel/copier.rb +23 -0
- data/lib/clowne/adapters/sequel/record_wrapper.rb +59 -0
- data/lib/clowne/cloner.rb +6 -4
- data/lib/clowne/declarations.rb +1 -0
- data/lib/clowne/declarations/exclude_association.rb +0 -5
- data/lib/clowne/declarations/include_association.rb +0 -2
- data/lib/clowne/declarations/init_as.rb +20 -0
- data/lib/clowne/declarations/trait.rb +2 -0
- data/lib/clowne/ext/orm_ext.rb +21 -0
- data/lib/clowne/ext/string_constantize.rb +2 -2
- data/lib/clowne/planner.rb +11 -4
- data/lib/clowne/version.rb +1 -1
- metadata +70 -4
data/gemfiles/jruby.gemfile
CHANGED
data/lib/clowne.rb
CHANGED
@@ -11,7 +11,8 @@ module Clowne
|
|
11
11
|
# List of built-in adapters
|
12
12
|
ADAPTERS = {
|
13
13
|
base: 'Base',
|
14
|
-
active_record: 'ActiveRecord'
|
14
|
+
active_record: 'ActiveRecord',
|
15
|
+
sequel: 'Sequel'
|
15
16
|
}.freeze
|
16
17
|
|
17
18
|
class << self
|
@@ -37,3 +38,4 @@ module Clowne
|
|
37
38
|
end
|
38
39
|
|
39
40
|
require 'clowne/adapters/active_record' if defined?(::ActiveRecord)
|
41
|
+
require 'clowne/adapters/sequel' if defined?(::Sequel)
|
@@ -1,26 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'clowne/ext/orm_ext'
|
4
|
+
|
3
5
|
module Clowne
|
4
6
|
module Adapters
|
5
7
|
# Cloning adapter for ActiveRecord
|
6
8
|
class ActiveRecord < Base
|
7
|
-
# Adds #cloner_class method to ActiveRecord::Base
|
8
|
-
module ActiveRecordExt
|
9
|
-
def cloner_class
|
10
|
-
return @_clowne_cloner if instance_variable_defined?(:@_clowne_cloner)
|
11
|
-
|
12
|
-
cloner = "#{name}Cloner".safe_constantize
|
13
|
-
return @_clowne_cloner = cloner if cloner && cloner <= Clowne::Cloner
|
14
|
-
|
15
|
-
@_clowne_cloner = superclass.cloner_class if superclass.respond_to?(:cloner_class)
|
16
|
-
end
|
17
|
-
end
|
18
9
|
end
|
19
10
|
end
|
20
11
|
end
|
21
12
|
|
22
13
|
ActiveSupport.on_load(:active_record) do
|
23
|
-
::ActiveRecord::Base.extend Clowne::
|
14
|
+
::ActiveRecord::Base.extend Clowne::Ext::ORMExt
|
24
15
|
end
|
25
16
|
|
26
17
|
require 'clowne/adapters/active_record/associations'
|
@@ -1,61 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'clowne/adapters/base/association'
|
4
|
+
|
3
5
|
module Clowne
|
4
6
|
module Adapters # :nodoc: all
|
5
7
|
class ActiveRecord
|
6
8
|
module Associations
|
7
|
-
class Base
|
8
|
-
# Params:
|
9
|
-
# +reflection+:: Association eflection object
|
10
|
-
# +source+:: Instance of cloned object (ex: User.new(posts: posts))
|
11
|
-
# +declaration+:: = Relation description
|
12
|
-
# (ex: Clowne::Declarations::IncludeAssociation.new(:posts))
|
13
|
-
# +params+:: = Instance of Clowne::Params
|
14
|
-
def initialize(reflection, source, declaration, params)
|
15
|
-
@source = source
|
16
|
-
@scope = declaration.scope
|
17
|
-
@clone_with = declaration.clone_with
|
18
|
-
@params = params
|
19
|
-
@association_name = declaration.name.to_s
|
20
|
-
@reflection = reflection
|
21
|
-
@cloner_options = params
|
22
|
-
@cloner_options.merge!(traits: declaration.traits) if declaration.traits
|
23
|
-
end
|
24
|
-
|
25
|
-
def call(_record)
|
26
|
-
raise NotImplementedError
|
27
|
-
end
|
28
|
-
|
29
|
-
def association
|
30
|
-
@_association ||= source.__send__(association_name)
|
31
|
-
end
|
32
|
-
|
33
|
-
def clone_one(child)
|
34
|
-
cloner = cloner_for(child)
|
35
|
-
cloner ? cloner.call(child, cloner_options) : child.dup
|
36
|
-
end
|
37
|
-
|
38
|
-
def with_scope
|
39
|
-
base_scope = association
|
40
|
-
if scope.is_a?(Symbol)
|
41
|
-
base_scope.__send__(scope)
|
42
|
-
elsif scope.is_a?(Proc)
|
43
|
-
base_scope.instance_exec(params, &scope) || base_scope
|
44
|
-
else
|
45
|
-
base_scope
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
9
|
+
class Base < Base::Association
|
49
10
|
private
|
50
11
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
return child.class.cloner_class if child.class.respond_to?(:cloner_class)
|
12
|
+
def clone_record(record)
|
13
|
+
record.dup
|
55
14
|
end
|
56
15
|
|
57
|
-
|
58
|
-
|
16
|
+
def init_scope
|
17
|
+
association
|
18
|
+
end
|
59
19
|
end
|
60
20
|
end
|
61
21
|
end
|
@@ -5,10 +5,16 @@ module Clowne
|
|
5
5
|
class ActiveRecord
|
6
6
|
module Associations
|
7
7
|
class HasOne < Base
|
8
|
+
# rubocop: disable Metrics/MethodLength
|
8
9
|
def call(record)
|
9
10
|
child = association
|
10
11
|
return record unless child
|
11
|
-
|
12
|
+
unless scope.nil?
|
13
|
+
warn(
|
14
|
+
'[Clowne] Has one association does not support scopes ' \
|
15
|
+
"(#{@association_name} for #{@source.class})"
|
16
|
+
)
|
17
|
+
end
|
12
18
|
|
13
19
|
child_clone = clone_one(child)
|
14
20
|
child_clone[:"#{reflection.foreign_key}"] = nil
|
@@ -16,6 +22,7 @@ module Clowne
|
|
16
22
|
|
17
23
|
record
|
18
24
|
end
|
25
|
+
# rubocop: enable Metrics/MethodLength
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -6,7 +6,10 @@ module Clowne
|
|
6
6
|
module Associations
|
7
7
|
class Noop < Base
|
8
8
|
def call(record)
|
9
|
-
warn(
|
9
|
+
warn(
|
10
|
+
"[Clowne] Reflection #{reflection.class.name} is not supported "\
|
11
|
+
"(#{@association_name} for #{@source.class})"
|
12
|
+
)
|
10
13
|
record
|
11
14
|
end
|
12
15
|
end
|
@@ -0,0 +1,33 @@
|
|
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, &block)
|
10
|
+
self.class.cloner_class.call(self, *args, &block)
|
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
|
data/lib/clowne/adapters/base.rb
CHANGED
@@ -19,10 +19,12 @@ module Clowne
|
|
19
19
|
registry.mapping[type] || raise("Uknown resolver #{type} for #{self}")
|
20
20
|
end
|
21
21
|
|
22
|
-
def register_resolver(type, resolver, after: nil, before: nil)
|
22
|
+
def register_resolver(type, resolver, after: nil, before: nil, prepend: nil)
|
23
23
|
registry.mapping[type] = resolver
|
24
24
|
|
25
|
-
if
|
25
|
+
if prepend
|
26
|
+
registry.unshift type
|
27
|
+
elsif after
|
26
28
|
registry.insert_after after, type
|
27
29
|
elsif before
|
28
30
|
registry.insert_before before, type
|
@@ -48,8 +50,8 @@ module Clowne
|
|
48
50
|
# +params+:: Custom params hash
|
49
51
|
def clone(source, plan, params: {})
|
50
52
|
declarations = plan.declarations
|
51
|
-
declarations.inject(
|
52
|
-
resolver_for(type).call(source, record, declaration, params: params)
|
53
|
+
declarations.inject(init_record(dup_source(source))) do |record, (type, declaration)|
|
54
|
+
resolver_for(type).call(source, record, declaration, params: params, adapter: self)
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
@@ -57,13 +59,18 @@ module Clowne
|
|
57
59
|
self.class.resolver_for(type)
|
58
60
|
end
|
59
61
|
|
60
|
-
|
61
|
-
def clone_record(source)
|
62
|
+
def dup_source(source)
|
62
63
|
source.dup
|
63
64
|
end
|
65
|
+
|
66
|
+
def init_record(record)
|
67
|
+
# Override in custom adapters
|
68
|
+
record
|
69
|
+
end
|
64
70
|
end
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|
74
|
+
require 'clowne/adapters/base/init_as'
|
68
75
|
require 'clowne/adapters/base/nullify'
|
69
76
|
require 'clowne/adapters/base/finalize'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Clowne
|
4
|
+
module Adapters # :nodoc: all
|
5
|
+
class Base
|
6
|
+
class Association
|
7
|
+
# Params:
|
8
|
+
# +reflection+:: Association eflection object
|
9
|
+
# +source+:: Instance of cloned object (ex: User.new(posts: posts))
|
10
|
+
# +declaration+:: = Relation description
|
11
|
+
# (ex: Clowne::Declarations::IncludeAssociation.new(:posts))
|
12
|
+
# +params+:: = Instance of Hash
|
13
|
+
def initialize(reflection, source, declaration, params)
|
14
|
+
@source = source
|
15
|
+
@scope = declaration.scope
|
16
|
+
@clone_with = declaration.clone_with
|
17
|
+
@params = params
|
18
|
+
@association_name = declaration.name.to_s
|
19
|
+
@reflection = reflection
|
20
|
+
@cloner_options = params
|
21
|
+
@cloner_options.merge!(traits: declaration.traits) if declaration.traits
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(_record)
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
def association
|
29
|
+
@_association ||= source.__send__(association_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def clone_one(child)
|
33
|
+
cloner = cloner_for(child)
|
34
|
+
cloner ? cloner.call(child, cloner_options) : clone_record(child)
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_scope
|
38
|
+
base_scope = init_scope
|
39
|
+
if scope.is_a?(Symbol)
|
40
|
+
base_scope.__send__(scope)
|
41
|
+
elsif scope.is_a?(Proc)
|
42
|
+
base_scope.instance_exec(params, &scope) || base_scope
|
43
|
+
else
|
44
|
+
base_scope
|
45
|
+
end.to_a
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def clone_record(_record)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
def init_scope
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
def cloner_for(child)
|
59
|
+
return clone_with if clone_with
|
60
|
+
|
61
|
+
return child.class.cloner_class if child.class.respond_to?(:cloner_class)
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :source, :scope, :clone_with, :params, :association_name,
|
65
|
+
:reflection, :cloner_options
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Clowne
|
4
|
+
module Adapters
|
5
|
+
class Base
|
6
|
+
module InitAs # :nodoc: all
|
7
|
+
# rubocop: disable Metrics/ParameterLists
|
8
|
+
def self.call(source, _record, declaration, params:, adapter:, **_options)
|
9
|
+
adapter.init_record(declaration.block.call(source, params))
|
10
|
+
end
|
11
|
+
# rubocop: enable Metrics/ParameterLists
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Clowne::Adapters::Base.register_resolver(
|
18
|
+
:init_as,
|
19
|
+
Clowne::Adapters::Base::InitAs,
|
20
|
+
prepend: true
|
21
|
+
)
|
@@ -11,7 +11,7 @@ module Clowne
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def insert_after(after, action)
|
14
|
-
|
14
|
+
actions.delete(action)
|
15
15
|
|
16
16
|
after_index = actions.find_index(after)
|
17
17
|
|
@@ -21,7 +21,7 @@ module Clowne
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def insert_before(before, action)
|
24
|
-
|
24
|
+
actions.delete(action)
|
25
25
|
|
26
26
|
before_index = actions.find_index(before)
|
27
27
|
|
@@ -31,12 +31,12 @@ module Clowne
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def append(action)
|
34
|
-
|
34
|
+
actions.delete(action)
|
35
35
|
actions.push action
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
38
|
+
def unshift(action)
|
39
|
+
actions.delete(action)
|
40
40
|
actions.unshift action
|
41
41
|
end
|
42
42
|
|
@@ -50,12 +50,6 @@ module Clowne
|
|
50
50
|
protected
|
51
51
|
|
52
52
|
attr_writer :mapping
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def validate_uniq!(action)
|
57
|
-
raise "Plan action already registered: #{action}" if actions.include?(action)
|
58
|
-
end
|
59
53
|
end
|
60
54
|
end
|
61
55
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'clowne/ext/orm_ext'
|
4
|
+
|
5
|
+
module Clowne
|
6
|
+
module Adapters
|
7
|
+
# Cloning adapter for Sequel
|
8
|
+
class Sequel < Base
|
9
|
+
def init_record(source)
|
10
|
+
RecordWrapper.new(source)
|
11
|
+
end
|
12
|
+
|
13
|
+
def dup_source(source)
|
14
|
+
Clowne::Adapters::Sequel::Copier.call(source)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
::Sequel::Model.extend Clowne::Ext::ORMExt
|
21
|
+
|
22
|
+
require 'clowne/adapters/sequel/associations'
|
23
|
+
require 'clowne/adapters/sequel/association'
|
24
|
+
require 'clowne/adapters/sequel/copier'
|
25
|
+
require 'clowne/adapters/sequel/record_wrapper'
|