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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +7 -0
  3. data/.gitattributes +1 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +17 -0
  6. data/.travis.yml +15 -2
  7. data/CHANGELOG.md +9 -2
  8. data/Gemfile +1 -0
  9. data/README.md +25 -381
  10. data/clowne.gemspec +1 -0
  11. data/docs/.rubocop.yml +12 -0
  12. data/docs/active_record.md +36 -0
  13. data/docs/alternatives.md +26 -0
  14. data/docs/architecture.md +141 -0
  15. data/docs/basic_example.md +66 -0
  16. data/docs/configuration.md +29 -0
  17. data/docs/customization.md +64 -0
  18. data/docs/exclude_association.md +63 -0
  19. data/docs/execution_order.md +14 -0
  20. data/docs/finalize.md +35 -0
  21. data/docs/implicit_cloner.md +36 -0
  22. data/docs/include_association.md +119 -0
  23. data/docs/init_as.md +36 -0
  24. data/docs/inline_configuration.md +38 -0
  25. data/docs/installation.md +16 -0
  26. data/docs/nullify.md +37 -0
  27. data/docs/sequel.md +56 -0
  28. data/docs/supported_adapters.md +13 -0
  29. data/docs/traits.md +28 -0
  30. data/docs/web/.gitignore +11 -0
  31. data/docs/web/core/Footer.js +92 -0
  32. data/docs/web/i18n/en.json +134 -0
  33. data/docs/web/package.json +14 -0
  34. data/docs/web/pages/en/help.js +50 -0
  35. data/docs/web/pages/en/index.js +231 -0
  36. data/docs/web/pages/en/users.js +47 -0
  37. data/docs/web/sidebars.json +30 -0
  38. data/docs/web/siteConfig.js +44 -0
  39. data/docs/web/static/css/custom.css +229 -0
  40. data/docs/web/static/fonts/FiraCode-Medium.woff +0 -0
  41. data/docs/web/static/fonts/FiraCode-Regular.woff +0 -0
  42. data/docs/web/static/fonts/StemText.woff +0 -0
  43. data/docs/web/static/fonts/StemTextBold.woff +0 -0
  44. data/docs/web/static/img/favicon/favicon.ico +0 -0
  45. data/docs/web/yarn.lock +1741 -0
  46. data/gemfiles/activerecord42.gemfile +1 -0
  47. data/gemfiles/jruby.gemfile +2 -0
  48. data/gemfiles/railsmaster.gemfile +1 -0
  49. data/lib/clowne.rb +3 -1
  50. data/lib/clowne/adapters/active_record.rb +3 -12
  51. data/lib/clowne/adapters/active_record/association.rb +1 -1
  52. data/lib/clowne/adapters/active_record/associations/base.rb +8 -48
  53. data/lib/clowne/adapters/active_record/associations/has_one.rb +8 -1
  54. data/lib/clowne/adapters/active_record/associations/noop.rb +4 -1
  55. data/lib/clowne/adapters/active_record/dsl.rb +33 -0
  56. data/lib/clowne/adapters/base.rb +13 -6
  57. data/lib/clowne/adapters/base/association.rb +69 -0
  58. data/lib/clowne/adapters/base/finalize.rb +1 -1
  59. data/lib/clowne/adapters/base/init_as.rb +21 -0
  60. data/lib/clowne/adapters/registry.rb +5 -11
  61. data/lib/clowne/adapters/sequel.rb +25 -0
  62. data/lib/clowne/adapters/sequel/association.rb +47 -0
  63. data/lib/clowne/adapters/sequel/associations.rb +26 -0
  64. data/lib/clowne/adapters/sequel/associations/base.rb +23 -0
  65. data/lib/clowne/adapters/sequel/associations/many_to_many.rb +19 -0
  66. data/lib/clowne/adapters/sequel/associations/noop.rb +16 -0
  67. data/lib/clowne/adapters/sequel/associations/one_to_many.rb +23 -0
  68. data/lib/clowne/adapters/sequel/associations/one_to_one.rb +23 -0
  69. data/lib/clowne/adapters/sequel/copier.rb +23 -0
  70. data/lib/clowne/adapters/sequel/record_wrapper.rb +59 -0
  71. data/lib/clowne/cloner.rb +6 -4
  72. data/lib/clowne/declarations.rb +1 -0
  73. data/lib/clowne/declarations/exclude_association.rb +0 -5
  74. data/lib/clowne/declarations/include_association.rb +0 -2
  75. data/lib/clowne/declarations/init_as.rb +20 -0
  76. data/lib/clowne/declarations/trait.rb +2 -0
  77. data/lib/clowne/ext/orm_ext.rb +21 -0
  78. data/lib/clowne/ext/string_constantize.rb +2 -2
  79. data/lib/clowne/planner.rb +11 -4
  80. data/lib/clowne/version.rb +1 -1
  81. metadata +70 -4
@@ -1,6 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'activerecord', '~> 4.2'
4
+ gem 'sequel', '>= 5.0'
4
5
  gem 'sqlite3'
5
6
 
6
7
  gemspec path: '..'
@@ -1,6 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'activerecord-jdbcsqlite3-adapter', '~> 50.0'
4
+ gem 'jdbc-sqlite3'
4
5
  gem 'activerecord', '~> 5.0.0'
6
+ gem 'sequel', '>= 5.0'
5
7
 
6
8
  gemspec path: '..'
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'arel', github: 'rails/arel'
4
4
  gem 'rails', github: 'rails/rails'
5
+ gem 'sequel', github: 'jeremyevans/sequel'
5
6
  gem 'sqlite3'
6
7
 
7
8
  gemspec path: '..'
@@ -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::Adapters::ActiveRecord::ActiveRecordExt
14
+ ::ActiveRecord::Base.extend Clowne::Ext::ORMExt
24
15
  end
25
16
 
26
17
  require 'clowne/adapters/active_record/associations'
@@ -7,7 +7,7 @@ module Clowne
7
7
 
8
8
  class Association
9
9
  class << self
10
- def call(source, record, declaration, params:)
10
+ def call(source, record, declaration, params:, **_options)
11
11
  reflection = source.class.reflections[declaration.name.to_s]
12
12
 
13
13
  if reflection.nil?
@@ -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 cloner_for(child)
52
- return clone_with if clone_with
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
- attr_reader :source, :scope, :clone_with, :params, :association_name,
58
- :reflection, :cloner_options
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
- warn '[Clowne] Has one association does not support scopes' unless scope.nil?
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("[Clowne] Reflection #{reflection.class.name} is not supported")
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
@@ -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 after
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(clone_record(source)) do |record, (type, declaration)|
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
- # Return #dup if any
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
@@ -4,7 +4,7 @@ module Clowne
4
4
  module Adapters
5
5
  class Base
6
6
  module Finalize # :nodoc: all
7
- def self.call(source, record, declaration, params:)
7
+ def self.call(source, record, declaration, params:, **_options)
8
8
  declaration.block.call(source, record, params)
9
9
  record
10
10
  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
- validate_uniq!(action)
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
- validate_uniq!(action)
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
- validate_uniq!(action)
34
+ actions.delete(action)
35
35
  actions.push action
36
36
  end
37
37
 
38
- def prepend(action)
39
- validate_uniq!(action)
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'