recess 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in recess.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Brian Leonard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,83 @@
1
+ ## Recess
2
+
3
+ You know... to break up your classes.
4
+
5
+ ### Inside
6
+
7
+ You can make classes that completely encapsulate behavior and have a defined interface with a parent object.
8
+
9
+ For example, let's define the behavior.
10
+
11
+ class TestInside1 < Recess::Inside::Base
12
+ parent_instance :foo
13
+
14
+ def my_method
15
+ "yes: #{foo}"
16
+ end
17
+
18
+ def other_method
19
+ "no"
20
+ end
21
+ end
22
+
23
+ This has methods that interact with the parent's foo method. This is then included within the parent.
24
+ The parent also declares what method it wants from the inside class.
25
+
26
+ class TestContainer1
27
+ inside TestInside1, :my_method
28
+
29
+ def initialize(val)
30
+ @val = val
31
+ end
32
+
33
+ def foo
34
+ @val
35
+ end
36
+
37
+ end
38
+
39
+ Technically, calling the inside method, defines a method on the parent that delegates to the inner class. Note that in the above other_method is not actually available on the parent, but my_method is.
40
+ The parent_instance method does something similar to get/set data on the parent.
41
+
42
+ Why would you want to do such a thing?
43
+
44
+ * TestInside1 is completely testable as a standalone class
45
+ * The inner class can be shared among models but the surface area of the inside class is minimized to only what is needed
46
+ * Understanding dependencies through these strict mechanisms helps understand the impact of changes to shared code
47
+
48
+ ### Outside
49
+
50
+ The other functionality is for class methods and stretches the metaphor a bit.
51
+
52
+ module TestOutside1
53
+ recess_group :something do
54
+ def self.test_class_method
55
+ "class_level"
56
+ end
57
+
58
+ def test_instance_method
59
+ "instance_level"
60
+ end
61
+ end
62
+
63
+ recess_group :else do
64
+ def self.other_test_class_method
65
+ "class_level2"
66
+ end
67
+
68
+ def other_test_instance_method
69
+ "instance_level2"
70
+ end
71
+ end
72
+ end
73
+
74
+ class TestOutsideExample1
75
+ outside TestOutside1, :something
76
+ end
77
+
78
+ It's like doing an include but with a certain name. In the above, only the :something group is included into TestOutsideExample1.
79
+ Again, this is mainly about understanding dependencies.
80
+
81
+ Copyright (c) 2011 Brian Leonard, released under the MIT license
82
+
83
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,133 @@
1
+ module Recess::Inside
2
+ module ClassMethods
3
+ def recess_inside_instance_methods(from_base=nil)
4
+ return [] unless @recess_inside_instance_methods
5
+ return @recess_inside_instance_methods.keys unless from_base
6
+ (@recess_inside_base_classes[from_base] || []).uniq
7
+ end
8
+
9
+ def recess_inside_objects_options
10
+ @recess_inside_objects_options ||= {}
11
+ end
12
+
13
+ def inside(what, *args)
14
+ options = args.last.is_a?(::Hash) ? args.pop : {}
15
+ object_clazz = what
16
+ recess_inside_objects_options[object_clazz.to_s] = options
17
+
18
+ @recess_inside_instance_methods ||= {}
19
+ @recess_inside_base_classes ||= {}
20
+
21
+ unless @recess_inside_base_classes[object_clazz]
22
+ if object_clazz.respond_to?(:insided)
23
+ if object_clazz.method(:insided).arity == 1
24
+ object_clazz.insided(self)
25
+ else
26
+ object_clazz.insided(self, args)
27
+ end
28
+ end
29
+ end
30
+
31
+ object_clazz.all_parent_instance_methods.each do |used|
32
+ @recess_inside_base_classes[object_clazz] ||= []
33
+ @recess_inside_base_classes[object_clazz] << used
34
+
35
+ @recess_inside_instance_methods[used] ||= []
36
+ @recess_inside_instance_methods[used] << object_clazz
37
+ end
38
+
39
+ unless method_defined? :recess_inside_instance_objects
40
+ define_method(:recess_inside_instance_objects) do |clazz|
41
+ data_method = self.class.recess_inside_objects_options[clazz.to_s][:data]
42
+ @recess_inside_instance_objects ||= {}
43
+ @recess_inside_instance_objects[clazz.to_s] ||= clazz.new(self, data_method)
44
+ end
45
+ end
46
+
47
+ args.each do |delegated|
48
+ the_alias = object_clazz.parent_aliased_instance_methods[delegated.to_s]
49
+ if the_alias and not method_defined?(the_alias)
50
+ if method_defined?(delegated)
51
+ class_eval("alias #{the_alias} #{delegated}")
52
+ elsif respond_to?(:column_names) # TODO: causing crash on load --- and column_names.include?(delegated.to_s)
53
+ define_method the_alias do
54
+ read_attribute(delegated)
55
+ end
56
+ end
57
+ end
58
+
59
+ define_method delegated do |*args|
60
+ recess_inside_instance_objects(object_clazz).send(delegated, *args)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ class Base
67
+ def self.all_parent_instance_methods
68
+ (parent_instance_methods + parent_aliased_instance_methods.keys).collect(&:to_s).uniq
69
+ end
70
+
71
+ def self.parent_instance_methods
72
+ @parent_instance_methods ||= []
73
+ end
74
+
75
+ def self.parent_aliased_instance_methods
76
+ @parent_aliased_instance_methods ||= {}
77
+ end
78
+
79
+ def self.parent_instance meth
80
+ parent_instance_methods << meth.to_s
81
+ define_method meth do |*args|
82
+ parent_data.send(meth, *args)
83
+ end
84
+ end
85
+
86
+ def self.alias_parent_instance super_meth, meth
87
+ parent_instance_methods << meth.to_s
88
+ the_alias = "#{meth}_before_recess"
89
+ parent_aliased_instance_methods[meth.to_s] = the_alias
90
+ define_method super_meth do |*args|
91
+ if parent_data.respond_to?(the_alias)
92
+ parent_data.send(the_alias, *args)
93
+ else
94
+ parent_data.send(meth, *args)
95
+ end
96
+ end
97
+ end
98
+
99
+ def initialize(instance, data_method = nil)
100
+ @instance = instance
101
+
102
+ if data_method
103
+ if data_method.is_a? Array
104
+ @data_args = data_method
105
+ else
106
+ @data_args = [data_method]
107
+ end
108
+ else
109
+ @data_args = nil
110
+ end
111
+ end
112
+
113
+ protected
114
+
115
+ def parent_root
116
+ @instance
117
+ end
118
+
119
+ def data_args
120
+ @data_args
121
+ end
122
+
123
+ def parent_data
124
+ if data_args
125
+ parent_root.send(*data_args)
126
+ else
127
+ parent_root
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ Object.send :include, Recess::Inside::ClassMethods
@@ -0,0 +1,25 @@
1
+ module Recess::Outside
2
+ module ClassMethods
3
+ def recess_group(name, &block)
4
+ @recess_groups ||= {}
5
+ @recess_groups[name.to_s] = block
6
+ end
7
+
8
+ def outside(what, *args)
9
+ options = args.last.is_a?(::Hash) ? args.pop : {}
10
+ object_clazz = what
11
+
12
+ groups = object_clazz.instance_variable_get("@recess_groups")
13
+ raise "Missing recess_groups directive in #{object_clazz}" unless groups
14
+
15
+ args.each do |trait_name|
16
+ macro = groups[trait_name.to_s]
17
+ raise "Missing recess group name: #{trait_name}" unless macro
18
+
19
+ class_exec(&macro)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ Object.send :include, Recess::Outside::ClassMethods
@@ -0,0 +1,3 @@
1
+ module Recess
2
+ VERSION = "0.0.1"
3
+ end
data/lib/recess.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "recess/version"
2
+
3
+ module Recess
4
+ module Inside
5
+
6
+ end
7
+ end
8
+
9
+ require "recess/outside"
10
+ require "recess/inside"
11
+
data/recess.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "recess/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "recess"
7
+ s.version = Recess::VERSION
8
+ s.authors = ["Brian Leonard"]
9
+ s.email = ["brian@bleonard.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{You know, to break up your classes.}
12
+ s.description = %q{You know, to break up your classes.}
13
+
14
+ s.rubyforge_project = "recess"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency 'rspec', '1.3.0'
22
+ s.add_development_dependency 'mocha', '0.9.8'
23
+
24
+ # specify any dependencies here; for example:
25
+ # s.add_development_dependency "rspec"
26
+ # s.add_runtime_dependency "rest-client"
27
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ class TestInside1 < Recess::Inside::Base
4
+ parent_instance :foo
5
+ parent_instance :foo=
6
+
7
+ def my_method
8
+ "yes: #{foo}"
9
+ end
10
+
11
+ def other_method
12
+ "no"
13
+ end
14
+ end
15
+
16
+ class TestInside2 < Recess::Inside::Base
17
+ alias_parent_instance :super_foo, :foo
18
+
19
+ def foo
20
+ "super: #{super_foo}"
21
+ end
22
+ end
23
+
24
+ class TestContainer1
25
+ inside TestInside1, :my_method
26
+
27
+ def initialize(val)
28
+ @val = val
29
+ end
30
+
31
+ def foo
32
+ @val
33
+ end
34
+ end
35
+
36
+ class TestContainer2
37
+ inside TestInside1, :my_method, :data => :whatever
38
+
39
+ def initialize(val)
40
+ @val = val
41
+ end
42
+
43
+ def foo
44
+ @val
45
+ end
46
+
47
+ def whatever
48
+ @whatever ||= TestExample1Data.new
49
+ end
50
+ end
51
+
52
+ class TestContainer3
53
+ # can't put it before as it prevents aliasing
54
+
55
+ def foo
56
+ "container"
57
+ end
58
+
59
+ # override, but call back to the above
60
+ inside TestInside2, :foo
61
+ end
62
+
63
+
64
+ class TestInside4 < Recess::Inside::Base
65
+
66
+ def self.insided(model)
67
+ model.callback_here("yeah")
68
+ end
69
+
70
+ def foo
71
+ "here"
72
+ end
73
+ end
74
+
75
+ class TestContainer4
76
+ def self.callback_here(val)
77
+ @called_value = val
78
+ end
79
+
80
+ def self.called_value
81
+ @called_value
82
+ end
83
+
84
+ inside TestInside4, :foo
85
+ end
86
+
87
+ class TestExample1Data
88
+ def initialize(val="data")
89
+ @val = val
90
+ end
91
+ def foo
92
+ @val
93
+ end
94
+ end
95
+
96
+ class TestExample1
97
+ def foo=val
98
+ @foo = val
99
+ end
100
+ def foo
101
+ @foo ||= "one"
102
+ end
103
+
104
+ def bar
105
+ "two"
106
+ end
107
+
108
+ def data
109
+ @data ||= TestExample1Data.new
110
+ end
111
+
112
+ def initd(val)
113
+ @initd ||= TestExample1Data.new(val)
114
+ end
115
+ end
116
+
117
+ describe "Containers" do
118
+ it "should know what properties are requested of it" do
119
+ TestContainer1.recess_inside_instance_methods.should =~ ["foo", "foo="]
120
+ TestContainer1.recess_inside_instance_methods(TestInside1).should =~ ["foo", "foo="]
121
+ TestContainer3.recess_inside_instance_methods(TestInside2).should =~ ["foo"]
122
+ end
123
+
124
+ it "should allow call through to super" do
125
+ container = TestContainer3.new
126
+ container.foo.should == "super: container"
127
+ end
128
+
129
+ it "should pass through to the inside" do
130
+ container = TestContainer1.new("what")
131
+ container.foo.should == "what"
132
+ container.my_method.should == "yes: what"
133
+
134
+ lambda { container.other_method }.should raise_error
135
+ end
136
+
137
+ it "should be able to take in data attribute" do
138
+ container = TestContainer2.new("what")
139
+ container.foo.should == "what"
140
+ container.whatever.foo.should == "data"
141
+ container.my_method.should == "yes: data"
142
+
143
+ lambda { container.other_method }.should raise_error
144
+ end
145
+
146
+ it "should call back the container when included" do
147
+ TestContainer4.called_value.should == "yeah"
148
+ end
149
+ end
150
+
151
+ describe "Base" do
152
+ it "should pass through declared attribtues to the init'd class" do
153
+ example = TestExample1.new
154
+ test = TestInside1.new(example)
155
+ test.foo.should == "one"
156
+ test.foo = "set"
157
+ test.foo.should == "set"
158
+
159
+ lambda { test.bar }.should raise_error
160
+ end
161
+
162
+ it "should use the data if noted" do
163
+ example = TestExample1.new
164
+ test = TestInside1.new(example, :data)
165
+ test.foo.should == "data"
166
+
167
+ example = TestExample1.new
168
+ test = TestInside1.new(example, [:data])
169
+ test.foo.should == "data"
170
+ end
171
+
172
+ it "should use the data and args" do
173
+ example = TestExample1.new
174
+ test = TestInside1.new(example, [:initd, "sent"])
175
+ test.foo.should == "sent"
176
+ end
177
+
178
+ it "should call through to the parent" do
179
+ example = TestExample1.new
180
+ test = TestInside2.new(example)
181
+ test.foo.should == "super: one"
182
+ end
183
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ module TestOutside1
4
+ recess_group :something do
5
+ def self.test_class_method
6
+ "class_level"
7
+ end
8
+
9
+ def test_instance_method
10
+ "instance_level"
11
+ end
12
+ end
13
+
14
+ recess_group :else do
15
+ def self.other_test_class_method
16
+ "class_level2"
17
+ end
18
+
19
+ def other_test_instance_method
20
+ "instance_level2"
21
+ end
22
+ end
23
+ end
24
+
25
+ class TestOutsideExample1
26
+ outside TestOutside1, :something
27
+ end
28
+
29
+ describe "Outside methods" do
30
+ it "should inject those methods" do
31
+ TestOutsideExample1.test_class_method.should == "class_level"
32
+ TestOutsideExample1.new.test_instance_method.should == "instance_level"
33
+
34
+ lambda { TestOutsideExample1.other_test_class_method }.should raise_error
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.setup
5
+
6
+ require 'spec/autorun'
7
+
8
+ require 'recess'
9
+
10
+ Spec::Runner.configure do |config|
11
+
12
+ end
data/spec/test_spec.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Setup Rspec" do
4
+ it "should work" do
5
+ (1+1).should == 2
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: recess
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Brian Leonard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-30 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - "="
27
+ - !ruby/object:Gem::Version
28
+ hash: 27
29
+ segments:
30
+ - 1
31
+ - 3
32
+ - 0
33
+ version: 1.3.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: mocha
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - "="
43
+ - !ruby/object:Gem::Version
44
+ hash: 43
45
+ segments:
46
+ - 0
47
+ - 9
48
+ - 8
49
+ version: 0.9.8
50
+ type: :development
51
+ version_requirements: *id002
52
+ description: You know, to break up your classes.
53
+ email:
54
+ - brian@bleonard.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - MIT-LICENSE
65
+ - README.mdown
66
+ - Rakefile
67
+ - lib/recess.rb
68
+ - lib/recess/inside.rb
69
+ - lib/recess/outside.rb
70
+ - lib/recess/version.rb
71
+ - recess.gemspec
72
+ - spec/inside_spec.rb
73
+ - spec/outside_spec.rb
74
+ - spec/spec_helper.rb
75
+ - spec/test_spec.rb
76
+ homepage: ""
77
+ licenses: []
78
+
79
+ post_install_message:
80
+ rdoc_options: []
81
+
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ requirements: []
103
+
104
+ rubyforge_project: recess
105
+ rubygems_version: 1.8.21
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: You know, to break up your classes.
109
+ test_files:
110
+ - spec/inside_spec.rb
111
+ - spec/outside_spec.rb
112
+ - spec/spec_helper.rb
113
+ - spec/test_spec.rb