strong_concerns 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,11 +8,13 @@ But this pattern is easilly can be abused:
8
8
  when you just spliting object behavior physicaly, not logicaly, you've got messy code.
9
9
 
10
10
  The core point is tracking the internal interface between an object and a concern!
11
- Minimal & explicit interface forward you to craft more clean and reusable concerns.
11
+ Minimal & explicit interface force you to craft more clean and reusable concerns.
12
12
  The ideal concern is ruby's Enumerable, which require only one method to be implemented in hosting class.
13
13
 
14
14
  Gem **strong_concerns** is technically helping you to create concerns in a right way.
15
15
 
16
+ For dependency tracing you should turn on role before usage (DCI inspired)!
17
+
16
18
  ## Installation
17
19
 
18
20
  Add this line to your application's Gemfile:
@@ -31,11 +33,15 @@ Or install it yourself as:
31
33
 
32
34
  ``` ruby
33
35
 
34
- module AgeAssertions
36
+ module Old
35
37
 
36
38
  # list methods required for concern
37
39
  def self.require_methods
38
- %w[age]
40
+ %w[bith_date]
41
+ end
42
+
43
+ def age
44
+ ((Date.today - bith_date)/365.0).to_i
39
45
  end
40
46
 
41
47
  def young?
@@ -61,23 +67,16 @@ module Searchable
61
67
  end
62
68
  end
63
69
 
64
- class Person
65
-
66
- attr :age
67
- attr :name
70
+ class Person < Struct.new(:name, :bith_date)
68
71
 
69
72
  def self.all
70
73
  [new('nicola', 33), new('ivan', 33)]
71
74
  end
72
75
 
73
- def initialize( name, age)
74
- @name, @age = name, age
75
- end
76
-
77
76
  extend StrongConcerns
78
77
 
79
- concern AgeAssertions,
80
- exports_methods: %w[young? reproductive?],
78
+ concern Old,
79
+ exports_methods: %w[age young? reproductive?],
81
80
  old: 70,
82
81
  young: 14
83
82
 
@@ -85,12 +84,17 @@ class Person
85
84
  exports_methods: %w[find_by_name]
86
85
  end
87
86
 
88
- nicola = Person.new('nicola', 33)
87
+ nicola = Person.new('nicola', Date.parse('1980-03-05'))
88
+
89
+ nicola.reproductive? #=> raise RoleNotActive error
90
+ nicola.as(Old)
91
+
89
92
  if nicola.reproductive?
90
93
  puts "Cool, make me a child!"
91
94
  end
92
95
 
93
- Person.find_by_name('nicola') #=> Person(name: 'nicola')
96
+ Person.as(Searchable)
97
+ .find_by_name('nicola') #=> Person(name: 'nicola')
94
98
  ```
95
99
 
96
100
  ## Contributing
@@ -1,50 +1,75 @@
1
- require 'forwardable'
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
2
2
  require "strong_concerns/version"
3
3
 
4
4
  module StrongConcerns
5
- class Intermediate
6
- extend Forwardable
7
- attr_accessor :options
5
+ class RoleNotActive < StandardError; end
8
6
 
9
- def initialize(subject, options)
10
- @__subject = subject
11
- @options = options
7
+ autoload :Intermediate, 'strong_concerns/intermediate'
8
+ autoload :Reflection, 'strong_concerns/reflection'
9
+
10
+ module InstanceMethods
11
+ def as(mod)
12
+ role_instance(mod).activate
13
+ self
12
14
  end
13
15
 
14
- def method_missing(meth)
15
- raise NameError.new("Looks like you not list method <#{meth}> in self.require_methods of concern or misspelled it")
16
+ def role_instances
17
+ @role_instances ||= {}
16
18
  end
17
19
 
18
- def inspect
19
- "Intermediate<#{self.methods}>"
20
+ def role_instance(mod)
21
+ role_instances[mod] ||= self.class
22
+ .find_instance_role(mod)
23
+ .instance(self)
20
24
  end
21
25
  end
22
26
 
23
- def class_concern(mod, options)
24
- intermediate_class = prepare_intermediate(mod)
25
- options.fetch(:exports_methods).each do |meth|
26
- self.define_singleton_method meth do |*args, &block|
27
- ((@__strong_concerns ||= {})[mod.to_s] ||= intermediate_class.new(self, options)).send(meth,*args, &block)
28
- end
29
- end
27
+ def self.extended(base)
28
+ base.send(:extend, Reflection)
29
+ base.send(:extend, ClassMethods)
30
+ base.send(:include, InstanceMethods)
31
+ super
30
32
  end
31
33
 
32
34
  def concern(mod, options)
33
- intermediate_class = prepare_intermediate(mod)
35
+ self.add_instance_role(mod, options)
34
36
  options.fetch(:exports_methods).each do |meth|
35
37
  self.send(:define_method, meth) do |*args, &block|
36
- ((@__strong_concerns ||= {})[mod.to_s] ||= intermediate_class.new(self, options)).send(meth,*args, &block)
38
+ inter = role_instance(mod)
39
+ unless inter.active?
40
+ raise RoleNotActive.new("Your call method <#{meth}> of inactive role <#{mod.name}>!")
41
+ end
42
+ inter.send(meth,*args, &block)
37
43
  end
38
44
  end
39
45
  end
40
46
 
41
- private
47
+ module ClassMethods
48
+ def as(mod)
49
+ role_instance(mod).activate
50
+ self
51
+ end
52
+
53
+ def role_instances
54
+ @role_instances ||= {}
55
+ end
56
+
57
+ def role_instance(mod)
58
+ role_instances[mod] ||= self.find_class_role(mod).instance(self)
59
+ end
60
+ end
42
61
 
43
- def prepare_intermediate(mod)
44
- Class.new(Intermediate).tap do |kls|
45
- kls.send(:include, mod)
46
- meths = mod.require_methods
47
- kls.def_delegators :@__subject, *meths
62
+ def class_concern(mod, options)
63
+ self.add_class_role(mod, options)
64
+ options.fetch(:exports_methods).each do |meth|
65
+ self.define_singleton_method meth do |*args, &block|
66
+ inter = role_instance(mod)
67
+ unless inter.active?
68
+ raise RoleNotActive.new("Your call method <#{meth}> of inactive role <#{mod.name}>!")
69
+ end
70
+ inter.inactivate
71
+ inter.send(meth,*args, &block)
72
+ end
48
73
  end
49
74
  end
50
75
  end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+
3
+ # two way proxy
4
+ module StrongConcerns
5
+ class Intermediate
6
+ extend Forwardable
7
+
8
+ def self.prepare(mod)
9
+ Class.new(Intermediate).tap do |kls|
10
+ kls.send(:include, mod)
11
+ meths = mod.require_methods
12
+ kls.def_delegators :@__subject__, *meths
13
+ end
14
+ end
15
+
16
+ attr_accessor :options
17
+
18
+ def activate
19
+ @active = true
20
+ end
21
+
22
+ def inactivate
23
+ @active = false
24
+ end
25
+
26
+ def active?
27
+ @active
28
+ end
29
+
30
+ def inactive?
31
+ not active?
32
+ end
33
+
34
+ def initialize(subject, options)
35
+ @__subject__ = subject
36
+ @options = options
37
+ @active = false
38
+ end
39
+
40
+ def method_missing(meth)
41
+ raise NameError.new("Looks like you not list method <#{meth}> in self.require_methods of concern or misspelled it")
42
+ end
43
+
44
+ def inspect
45
+ "Intermediate<#{self.methods}>"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ module StrongConcerns
2
+ class Role
3
+ attr :mod
4
+
5
+ def initialize(mod, options)
6
+ @mod = mod
7
+ @options = options
8
+ end
9
+
10
+ def intermediate_class
11
+ @intermediate_class ||= Intermediate.prepare(@mod)
12
+ end
13
+
14
+ def instance(subject)
15
+ intermediate_class.new(subject, @options)
16
+ end
17
+ end
18
+
19
+ module Reflection
20
+ def add_class_role(mod, options)
21
+ self.class_roles[mod] = Role.new(mod, options)
22
+ end
23
+
24
+ def add_instance_role(mod, options)
25
+ self.instance_roles[mod] = Role.new(mod, options)
26
+ end
27
+
28
+ def find_instance_role(mod)
29
+ instance_roles[mod]
30
+ end
31
+
32
+ def find_class_role(mod)
33
+ class_roles[mod]
34
+ end
35
+
36
+ def instance_roles
37
+ @instance_roles ||= {}
38
+ end
39
+
40
+ def class_roles
41
+ @class_roles ||= {}
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module StrongConcerns
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ describe StrongConcerns::Intermediate do
3
+ class A
4
+ def meth
5
+ "ups"
6
+ end
7
+ end
8
+
9
+ module B
10
+ def self.require_methods
11
+ %w[meth]
12
+ end
13
+
14
+ def decorated_meth
15
+ "#{meth}#{options[:opt1]}"
16
+ end
17
+ end
18
+
19
+ subject do
20
+ obj = A.new
21
+ kls = described_class.prepare(B)
22
+ kls.new(obj, opt1: '!!!')
23
+ end
24
+
25
+ it "should turn of/on" do
26
+ subject.should_not be_active
27
+ subject.activate
28
+ subject.should be_active
29
+ subject.inactivate
30
+ subject.should be_inactive
31
+ end
32
+
33
+ it "should delegate methods" do
34
+ subject.meth.should == 'ups'
35
+ end
36
+
37
+ it "should call role methods" do
38
+ subject.decorated_meth.should == 'ups!!!'
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ describe StrongConcerns::Reflection do
3
+ class A
4
+ extend StrongConcerns::Reflection
5
+ def meth
6
+ "ups"
7
+ end
8
+ end
9
+
10
+ module B
11
+ def self.require_methods
12
+ %w[meth]
13
+ end
14
+
15
+ def decorated_meth
16
+ "#{meth}#{options[:opt1]}"
17
+ end
18
+ end
19
+
20
+ subject { A }
21
+
22
+ it "instance roles" do
23
+ subject.instance_roles.should be_empty
24
+ subject.add_instance_role(B, opt1: 'val1')
25
+ role = subject.find_instance_role(B)
26
+ role.should be_a(StrongConcerns::Role)
27
+ end
28
+
29
+ it "class roles" do
30
+ subject.class_roles.should be_empty
31
+ subject.add_class_role(B, opt1: 'val1')
32
+ role = subject.find_class_role(B)
33
+ role.should be_a(StrongConcerns::Role)
34
+ end
35
+ end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'date'
2
3
 
3
4
  describe StrongConcerns do
4
5
  module FullNamed
@@ -11,9 +12,13 @@ describe StrongConcerns do
11
12
  end
12
13
  end
13
14
 
14
- module AgeAssertions
15
+ module Old
15
16
  def self.require_methods
16
- %w[age]
17
+ %w[bith_date]
18
+ end
19
+
20
+ def age
21
+ ((Date.today - bith_date)/365.0).to_i
17
22
  end
18
23
 
19
24
  def young?
@@ -44,23 +49,10 @@ describe StrongConcerns do
44
49
  end
45
50
  end
46
51
 
47
- class Person
48
-
49
- attr :first_name
50
- attr :last_name
51
- attr :age
52
-
53
- def initialize(first_name, last_name, age)
54
- @first_name = first_name
55
- @last_name = last_name
56
- @age = age
57
- end
52
+ class Person < Struct.new(:first_name, :last_name, :bith_date)
58
53
 
59
54
  def self.all
60
- [
61
- new('nicola','rhyzhikov', 33),
62
- new('ivan','ivanov', 33)
63
- ]
55
+ [ new('nicola','rhyzhikov', 33), new('ivan','ivanov', 33) ]
64
56
  end
65
57
 
66
58
  extend StrongConcerns
@@ -68,8 +60,8 @@ describe StrongConcerns do
68
60
  class_concern Searchable,
69
61
  exports_methods: %w[find_by_name]
70
62
 
71
- concern AgeAssertions,
72
- exports_methods: %w[young? reproductive? breaking],
63
+ concern Old,
64
+ exports_methods: %w[age young? reproductive? breaking],
73
65
  young: 14, old: 70
74
66
 
75
67
  concern FullNamed,
@@ -78,18 +70,39 @@ describe StrongConcerns do
78
70
  end
79
71
 
80
72
  example do
81
- Person.new('nicola', 'rhyzhikov', 33).tap do |nicola|
82
- nicola.full_name.should == "nicola rhyzhikov"
83
- nicola.should_not be_young
84
- nicola.should be_reproductive
85
-
86
- -> {
87
- nicola.breaking
88
- }.should raise_error(/not list method/)
89
- end
73
+ nicola = Person.new('nicola', 'rhyzhikov', Date.parse('1980-03-05'))
74
+
75
+ -> {
76
+ nicola.full_name
77
+ }.should raise_error(StrongConcerns::RoleNotActive)
78
+
79
+ nicola.as(FullNamed)
80
+
81
+ nicola.full_name.should == "nicola rhyzhikov"
82
+
83
+ nicola.as(Old)
84
+ nicola.should_not be_young
85
+ nicola.should be_reproductive
86
+
87
+ -> {
88
+ nicola.breaking
89
+ }.should raise_error(/not list method/)
90
90
  end
91
91
 
92
- example do
93
- Person.find_by_name('nicola').should_not be_empty
92
+ it "class_concern" do
93
+ -> {
94
+ Person
95
+ .find_by_name('nicola')
96
+ .should_not be_empty
97
+ }.should raise_error(StrongConcerns::RoleNotActive)
98
+
99
+ Person
100
+ .as(Searchable)
101
+ .find_by_name('nicola')
102
+ .should_not be_empty
103
+
104
+ -> {
105
+ Person.find_by_name('nicola')
106
+ }.should raise_error(StrongConcerns::RoleNotActive)
94
107
  end
95
108
  end
metadata CHANGED
@@ -1,43 +1,48 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strong_concerns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ prerelease:
5
+ version: 0.0.3
5
6
  platform: ruby
6
7
  authors:
7
8
  - niquola
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-07-16 00:00:00.000000000 Z
12
+ date: 2013-08-06 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
15
+ version_requirements: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
- type: :development
20
+ none: false
21
21
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
22
+ name: bundler
23
+ type: :development
24
+ requirement: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - ~>
25
27
  - !ruby/object:Gem::Version
26
28
  version: '1.3'
29
+ none: false
27
30
  - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
31
+ version_requirements: !ruby/object:Gem::Requirement
30
32
  requirements:
31
- - - '>='
33
+ - - ! '>='
32
34
  - !ruby/object:Gem::Version
33
35
  version: '0'
34
- type: :development
36
+ none: false
35
37
  prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
38
+ name: rake
39
+ type: :development
40
+ requirement: !ruby/object:Gem::Requirement
37
41
  requirements:
38
- - - '>='
42
+ - - ! '>='
39
43
  - !ruby/object:Gem::Version
40
44
  version: '0'
45
+ none: false
41
46
  description: Gem **strong_concerns** is technically helping you to create concerns
42
47
  in a right way, minimizing and making explicit interface between object and concern.
43
48
  email:
@@ -52,35 +57,48 @@ files:
52
57
  - README.md
53
58
  - Rakefile
54
59
  - lib/strong_concerns.rb
60
+ - lib/strong_concerns/intermediate.rb
61
+ - lib/strong_concerns/reflection.rb
55
62
  - lib/strong_concerns/version.rb
63
+ - spec/intermediate_spec.rb
64
+ - spec/reflection_spec.rb
56
65
  - spec/spec_helper.rb
57
66
  - spec/strong_concerns_spec.rb
58
67
  - strong_concerns.gemspec
59
68
  homepage: https://github.com/niquola/strong_concerns
60
69
  licenses:
61
70
  - MIT
62
- metadata: {}
63
71
  post_install_message:
64
72
  rdoc_options: []
65
73
  require_paths:
66
74
  - lib
67
75
  required_ruby_version: !ruby/object:Gem::Requirement
68
76
  requirements:
69
- - - '>='
77
+ - - ! '>='
70
78
  - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ hash: -2035030651887043314
71
82
  version: '0'
83
+ none: false
72
84
  required_rubygems_version: !ruby/object:Gem::Requirement
73
85
  requirements:
74
- - - '>='
86
+ - - ! '>='
75
87
  - !ruby/object:Gem::Version
88
+ segments:
89
+ - 0
90
+ hash: -2035030651887043314
76
91
  version: '0'
92
+ none: false
77
93
  requirements: []
78
94
  rubyforge_project:
79
- rubygems_version: 2.0.3
95
+ rubygems_version: 1.8.25
80
96
  signing_key:
81
- specification_version: 4
97
+ specification_version: 3
82
98
  summary: Gem **strong_concerns** is technically helping you to create concerns in
83
99
  a right way, minimizing and making explicit interface between object and concern.
84
100
  test_files:
101
+ - spec/intermediate_spec.rb
102
+ - spec/reflection_spec.rb
85
103
  - spec/spec_helper.rb
86
104
  - spec/strong_concerns_spec.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 26c6d7992012ae2a5204e3643a55de1bfbf98b43
4
- data.tar.gz: b3344d68b073077ff1849f31cb34969bee7ca56d
5
- SHA512:
6
- metadata.gz: 0e406c4ff2eadf7c553fed7a263a6a16e0773a34acf2d55185d8afc1458f32287fc72c7e3f53cfc0ef035b0b5cbdf7e2ecf6600f949d325f7cae5187ce264c02
7
- data.tar.gz: 6f172432785133c1e6cc7b3b4029aa04c58825dc73efd1e1b60951f506c58e54a1d2a3e3c11cee54a91a8d28c059eaad768d453faac544fcb621c99532de836a