oh_delegator 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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ N2VkNDdlZTA4M2QwMWM5MTM3ZjY2ZmNjMDJkMmNjYjM3ZWQzZTMzZA==
5
+ data.tar.gz: !binary |-
6
+ NWY1ZThhODUxMTQxNTRmZjQzNTdhMWM0Yzc1ZmQ4ODhkNDk2NjMwOA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZjViZWY0OWIxNDdiZWIzZjY5N2MwZGZhY2E1NmYxNjFiZjM0YTkwODEwMGNh
10
+ ZjlhOTZkZjA4NTEzMmEwODE2NWRmMzEzN2Y1N2Q2NzllNGZjNGI1ZmYzZDQw
11
+ NmVmMDRhZjNhZjM2MjUzMTM3NDU3Nzc4MjQ5ZmViNGEyZjIwY2E=
12
+ data.tar.gz: !binary |-
13
+ ZjJiMTM5ZDRkYjQzMTVjYWE4YmMxNmQ1YTljNDk1ODQ4MmVkMzc3MWFiMTNm
14
+ OThhNzE2ZDQyMjRlMTM0MzM2YWVmNWRkNTJmOTRhNmRlODcwMmIwZTk4Y2Mx
15
+ NjAzNmU2N2QzMjMwOTU4MGVjNTE4ODhmM2NhN2E3YjcyMDMyY2Q=
data/Readme.md ADDED
@@ -0,0 +1,90 @@
1
+ ## Inspiration
2
+
3
+ From [Wikipedia](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself):
4
+
5
+ > The DRY principle is stated as “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.”
6
+
7
+ ## Delegators vs Concerns
8
+
9
+ ActiveSupport::Concern is a convenient solution for splitting ActiveRecord models. It can serve as a single and authoritative feature representation, but it is ambiguous. Consider the following example:
10
+
11
+ ```ruby
12
+ # app/models/concerns/account_profile.rb
13
+ module AccountProfile
14
+ extend ActiveSupport::Concern
15
+
16
+ included do
17
+ has_many :profiles
18
+
19
+ validates :has_profile, inclusion: { in: [true, false] }
20
+ end
21
+
22
+ def active_profile
23
+ profiles.find_by(active: true)
24
+ end
25
+ end
26
+
27
+ # app/models/account.rb
28
+ class Account < ActiveRecord::Base
29
+ include AccountProfile
30
+ end
31
+
32
+ # app/controllers/accounts_controller.rb
33
+ ...
34
+ def index
35
+ @profile = @account.active_profile
36
+ end
37
+ ...
38
+ ```
39
+
40
+ When viewing the controller, the location of `@account.active_profile` seems ambiguous. The first thing that most developers would do is look at the `Account` model. Tools like ctags can help, but they have other limitations(e.g. cannot deal with duplicate method names in separate modules).
41
+
42
+ Delegators are more ideal as they comply with all three requirements of DRY. Also, it makes way for editor plugins to accurately track source location. However it lacks any convenient wrapper like `ActiveSupport::Concern` for working with ActiveRecord. This gem serves to fill that gap.
43
+
44
+ ## Usage
45
+
46
+ The delegators can be placed anywhere in your application's load path, the only requirement is that it must be nested under the delegatee object.
47
+
48
+ ```ruby
49
+ # app/delegators/account/profile_delegator.rb
50
+ class Account::ProfileDelegator < OhDelegator::Base
51
+ parent_scope do
52
+ has_many :profiles
53
+
54
+ validates :has_profile, inclusion: { in: [true, false] }
55
+ end
56
+
57
+ def active
58
+ profiles.find_by(active: true)
59
+ end
60
+ end
61
+
62
+ # app/models/account.rb
63
+ class Account < ActiveRecord::Base
64
+ oh_delegators :profile_delegator
65
+ end
66
+
67
+ # app/controllers/accounts_controller.rb
68
+ ...
69
+ def index
70
+ @profile = @account.profile_delegator.active
71
+ end
72
+ ...
73
+ ```
74
+
75
+ As we see, migrating code from an **ActiveRecord** model to a delegator is as simple as migrating a concern.
76
+
77
+ ## Extras
78
+
79
+ The delegatee object will be available inside the delegator. The name is inferred from the `parent` scope.
80
+
81
+ ```ruby
82
+ # app/delegators/account/profile_delegator.rb
83
+ class Account::ProfileDelegator < OhDelegator::Base
84
+ ...
85
+
86
+ def create_profile
87
+ Profile.create(account: account) # Account::ProfileDelegator.parent.name.downcase == 'account'
88
+ end
89
+ end
90
+ ```
@@ -0,0 +1,8 @@
1
+ require 'oh_delegator/parent_scope'
2
+ require 'oh_delegator/base'
3
+ require 'oh_delegator/delegatee'
4
+ require 'oh_delegator/orm'
5
+ require 'oh_delegator/railtie'
6
+
7
+ module OhDelegator
8
+ end
@@ -0,0 +1,14 @@
1
+ require 'delegate'
2
+
3
+ module OhDelegator
4
+ class Base < SimpleDelegator
5
+ extend ParentScope
6
+
7
+ class << self
8
+ def inherited(subclass)
9
+ delegatee_name = subclass.parent.name.downcase
10
+ define_method(delegatee_name, -> { __getobj__ })
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module OhDelegator
2
+ module Delegatee
3
+ def oh_delegators(*attributes)
4
+ attributes.each do |attribute|
5
+ # Read the delegator class alongwith the delegatee.
6
+ klass = "#{ name }::#{ attribute.to_s.classify }".constantize
7
+
8
+ define_method attribute do
9
+ instance_variable_name = "@#{ attribute }"
10
+ instance_variable = instance_variable_get(instance_variable_name)
11
+
12
+ return instance_variable if instance_variable
13
+
14
+ instance_variable_set(instance_variable_name, klass.new(self))
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module OhDelegator
2
+ class ORM
3
+ class << self
4
+ def setup(base)
5
+ base.class_eval do
6
+ extend OhDelegator::Delegatee
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module OhDelegator
2
+ module ParentScope
3
+ def parent_scope(&block)
4
+ parent.class_exec(&block)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails/railtie'
2
+
3
+ module OhDelegator
4
+ class Railtie < Rails::Railtie
5
+ initializer 'oh_delegator.orm.setup' do |app|
6
+ ActiveSupport.on_load(:active_record) do
7
+ OhDelegator::ORM.setup(self)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module OhDelegator
2
+ module Version
3
+ STRING = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'oh_delegator/version'
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = 'oh_delegator'
6
+ gem.version = OhDelegator::Version::STRING
7
+ gem.authors = ["BlackDuck Software"]
8
+ gem.email = ["info@blackducksoftware.com"]
9
+ gem.summary = %q{ActiveRecord model delegation}
10
+ gem.description = %q{Use delegators just like concerns}
11
+ gem.homepage = "https://github.com/blackducksw/oh_delegator"
12
+ gem.license = "MIT"
13
+
14
+ gem.files = `git ls-files -z`.split("\x0")
15
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^test/})
17
+ gem.require_paths = ["lib"]
18
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ describe OhDelegator::Base do
4
+ before do
5
+ class User
6
+ class ProfileCore < OhDelegator::Base
7
+ end
8
+ end
9
+
10
+ class User
11
+ extend OhDelegator::Delegatee
12
+ oh_delegators :profile_core
13
+
14
+ def type
15
+ :user
16
+ end
17
+ end
18
+ end
19
+
20
+ it 'must make the delegatee object available to the delegator' do
21
+ user = User.new
22
+ user.profile_core.user.must_equal user
23
+ end
24
+
25
+ it 'must delegate all instance methods to the delegatee' do
26
+ User.new.profile_core.type.must_equal :user
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ describe OhDelegator::Delegatee do
4
+ it 'must add an oh_delegators method when extended' do
5
+ class Person
6
+ extend OhDelegator::Delegatee
7
+ end
8
+
9
+ Person.methods.must_include :oh_delegators
10
+ end
11
+
12
+ describe 'oh_delegators' do
13
+ before do
14
+ class User
15
+ class ProfileCore < OhDelegator::Base
16
+ end
17
+ end
18
+
19
+ class User
20
+ extend OhDelegator::Delegatee
21
+ oh_delegators :profile_core
22
+ end
23
+ end
24
+
25
+ it 'must add a accessor for given delegators' do
26
+ User.new.profile_core.must_be :present?
27
+ end
28
+
29
+ it 'must make the accessor return a delegator object' do
30
+ User.new.profile_core.class.ancestors.first.must_equal User::ProfileCore
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ describe OhDelegator::ParentScope do
4
+ before do
5
+ class User
6
+ class << self
7
+ attr_reader :class_name
8
+
9
+ def set_class_name
10
+ @class_name = name
11
+ end
12
+ end
13
+
14
+ class ProfileCore < OhDelegator::Base
15
+ parent_scope do
16
+ set_class_name
17
+ end
18
+ end
19
+ end
20
+
21
+ class User
22
+ extend OhDelegator::Delegatee
23
+ oh_delegators :profile_core
24
+ end
25
+ end
26
+
27
+ describe 'any method defined in parent_scope' do
28
+ it 'must be called at compile time' do
29
+ User.class_name.must_be :present?
30
+ end
31
+
32
+ it 'must be called using delegatee\'s context' do
33
+ User.class_name.must_equal 'User'
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
2
+ require 'oh_delegator'
3
+ require 'minitest/autorun'
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oh_delegator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - BlackDuck Software
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Use delegators just like concerns
14
+ email:
15
+ - info@blackducksoftware.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Readme.md
21
+ - lib/oh_delegator.rb
22
+ - lib/oh_delegator/base.rb
23
+ - lib/oh_delegator/delegatee.rb
24
+ - lib/oh_delegator/orm.rb
25
+ - lib/oh_delegator/parent_scope.rb
26
+ - lib/oh_delegator/railtie.rb
27
+ - lib/oh_delegator/version.rb
28
+ - oh_delegator.gemspec
29
+ - test/lib/oh_delegator/base_test.rb
30
+ - test/lib/oh_delegator/delegatee_test.rb
31
+ - test/lib/oh_delegator/parent_scope_test.rb
32
+ - test/test_helper.rb
33
+ homepage: https://github.com/blackducksw/oh_delegator
34
+ licenses:
35
+ - MIT
36
+ metadata: {}
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 2.4.7
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: ActiveRecord model delegation
57
+ test_files:
58
+ - test/lib/oh_delegator/base_test.rb
59
+ - test/lib/oh_delegator/delegatee_test.rb
60
+ - test/lib/oh_delegator/parent_scope_test.rb
61
+ - test/test_helper.rb