class_inheritable 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,2 @@
1
+ .ignore
2
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in class_inheritable.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ class_inheritable (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ rspec (2.11.0)
11
+ rspec-core (~> 2.11.0)
12
+ rspec-expectations (~> 2.11.0)
13
+ rspec-mocks (~> 2.11.0)
14
+ rspec-core (2.11.0)
15
+ rspec-expectations (2.11.1)
16
+ diff-lcs (~> 1.1.3)
17
+ rspec-mocks (2.11.1)
18
+
19
+ PLATFORMS
20
+ ruby
21
+
22
+ DEPENDENCIES
23
+ class_inheritable!
24
+ rspec (~> 2.11.0)
data/MIT-LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2012 Jonathan Thomas
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,22 @@
1
+ Attempt to duplicate the functionality of Rails' old class_inheritable_ary
2
+ without monkey-patching base-classes or polluting target classes with
3
+ unexpected methods/attributes
4
+
5
+ Basic use-case:
6
+ irb -r "./lib/class_inheritable/array.rb"
7
+ class MyClass
8
+ ClassInheritable::Array.attach :to => self, :as => :foo
9
+ append_foo :bar
10
+ end
11
+ class MySubClass < MyClass
12
+ append_foo :baz
13
+ end
14
+
15
+ [:bar] == MyClass.foo
16
+ [:bar] == MyClass.new.foo
17
+
18
+ [:bar, :baz] == MySubClass.foo
19
+ [:bar, :baz] == MySubClass.new.foo
20
+
21
+ (See specs for more detailed use-cases.)
22
+ rspec ./spec/lib/class_inheritable/array_spec.rb
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ gemspec = eval(File.read("class_inheritable.gemspec"))
7
+
8
+ task :build => "#{gemspec.full_name}.gem"
9
+
10
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["class_inheritable.gemspec"] do
11
+ system "gem build class_inheritable.gemspec"
12
+ system "gem install class_inheritable-#{ClassInheritable::VERSION}.gem"
13
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "class_inheritable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "class_inheritable"
7
+ s.version = ClassInheritable::VERSION
8
+ s.authors = ["JayTeeSr"]
9
+ s.email = ["jaytee_sr_at_his-service_dot_net"]
10
+ s.homepage = "https://github.com/JayTeeSF/class_inheritable"
11
+ s.summary = %q{Automate management of i18n locale files}
12
+ s.description = %q{Export strings that need to be translated and integrate the completed translations}
13
+ s.rubyforge_project = "class_inheritable"
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.bindir = 'bin'
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ s.add_development_dependency "rspec", "~> 2.11.0"
22
+ end
@@ -0,0 +1,127 @@
1
+ module ClassInheritable
2
+ class Array
3
+
4
+ DEFAULT_ARRAY_NAME = 'class_inheritable_array'
5
+
6
+ attr_reader :caller_class
7
+ def initialize( _caller_class )
8
+ raise ArgumentError, "invalid calling class" if _caller_class <= ClassInheritable::Array
9
+ @caller_class = _caller_class
10
+ end
11
+
12
+ def attach( options = {} )
13
+ define_writer_for( caller_class, options )
14
+ define_appender_for( caller_class, options )
15
+ define_reader_for( caller_class, options )
16
+ define_instance_reader_for( caller_class, options )
17
+ _init( options )
18
+ end
19
+
20
+ class << self
21
+ # Factory method:
22
+ def attach( options = {}, &block )
23
+ caller_class = options[ :to ]
24
+ caller_class ||= calling_object( &block ) if block_given?
25
+ raise ArgumentError, ":to or block required" unless caller_class
26
+ new( caller_class ).attach( options )
27
+ end
28
+
29
+ # Class methods
30
+ def array_of_parent( child, options = {} )
31
+ array = array_name( options )
32
+ parent = child.send( :superclass )
33
+ return [] unless parent.respond_to?( array )
34
+
35
+ parent.send( array ) || []
36
+ end
37
+
38
+ def writer_name( options = {} )
39
+ options[ :writer_name ] || "#{array_name( options )}="
40
+ end
41
+
42
+ def appender_name( options = {} )
43
+ options[ :appender_name ] || "append_#{array_name( options )}"
44
+ end
45
+
46
+ def instance_reader_name( options = {} )
47
+ options[ :instance_reader_name ] || reader_name( options )
48
+ end
49
+
50
+ def reader_name( options = {} )
51
+ options[ :reader_name ] || array_name( options )
52
+ end
53
+
54
+ def array_name( options = {} )
55
+ options[ :as ] || DEFAULT_ARRAY_NAME
56
+ end
57
+
58
+ def eigenclass_for( base )
59
+ class << base; self; end
60
+ end
61
+
62
+ def calling_object( &block )
63
+ eval 'self', block.send(:binding)
64
+ end
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def _init( options = {} )
71
+ reader = ClassInheritable::Array.reader_name( options )
72
+ caller_class.send( reader )
73
+ self
74
+ end
75
+
76
+ def define_writer_for( base, options = {} )
77
+ eigenclass = ClassInheritable::Array.eigenclass_for( base )
78
+ writer = ClassInheritable::Array.writer_name( options )
79
+ array = ClassInheritable::Array.array_name( options )
80
+ eigenclass.class_eval do
81
+ define_method( "#{writer}" ) do |entries|
82
+ entries = [ entries ] unless entries.is_a?( ::Array )
83
+ instance_variable_set( "@#{array}", entries )
84
+ end
85
+ end
86
+ end
87
+
88
+ def define_appender_for( base, options = {} )
89
+ eigenclass = ClassInheritable::Array.eigenclass_for( base )
90
+ appender = ClassInheritable::Array.appender_name( options )
91
+ reader = ClassInheritable::Array.reader_name( options )
92
+ writer = ClassInheritable::Array.writer_name( options )
93
+ eigenclass.class_eval do
94
+ define_method( "#{appender}" ) do |entries|
95
+ entries = [ entries ] unless entries.is_a?( ::Array )
96
+ self.send( writer, self.send( reader ) | entries )
97
+ end
98
+ end
99
+ end
100
+
101
+ def define_instance_reader_for( base, options = {} )
102
+ reader = ClassInheritable::Array.instance_reader_name( options )
103
+ base.class_eval do
104
+ define_method( "#{reader}" ) do
105
+ self.class.send( "#{reader}" )
106
+ end
107
+ end
108
+ end
109
+
110
+ def define_reader_for( base, options = {} )
111
+ eigenclass = ClassInheritable::Array.eigenclass_for( base )
112
+ array = ClassInheritable::Array.array_name( options )
113
+ reader = ClassInheritable::Array.reader_name( options )
114
+ eigenclass.class_eval do
115
+ define_method( "#{reader}" ) do
116
+ unless instance_variable_get( "@#{array}" )
117
+ instance_variable_set(
118
+ "@#{array}", [] |
119
+ ClassInheritable::Array.array_of_parent( self, options )
120
+ )
121
+ end
122
+ instance_variable_get( "@#{array}" )
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module ClassInheritable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'class_inheritable/version'
2
+ require 'class_inheritable/array'
3
+ module ClassInheritable
4
+ end
@@ -0,0 +1,214 @@
1
+ require "spec_helper"
2
+ describe ClassInheritable::Array do
3
+ let(:some_class) { Class.new(Object) }
4
+ let(:some_class_expected_methods) {
5
+ base_name = ClassInheritable::Array::DEFAULT_ARRAY_NAME
6
+ [ "#{base_name}=", "append_#{base_name}", base_name ].map(&:to_sym)
7
+ }
8
+
9
+ context "initialization" do
10
+ context "without valid params" do
11
+ it "should raise an error" do
12
+ expect { ClassInheritable::Array.new }.to raise_error(ArgumentError)
13
+ end
14
+
15
+ context "with invalid params" do
16
+ it "should raise an error" do
17
+ expect { ClassInheritable::Array.new( ClassInheritable::Array ) }.to raise_error(ArgumentError)
18
+ expect { ClassInheritable::Array.new( Class.new( ClassInheritable::Array ) ) }.to raise_error(ArgumentError)
19
+ end
20
+ end
21
+ end
22
+
23
+ context "with valid params" do
24
+ it "should not raise an error" do
25
+ expect { ClassInheritable::Array.new( some_class ) }.not_to raise_error
26
+ end
27
+ end
28
+ end
29
+
30
+ context "attachment" do
31
+ context "without valid params" do
32
+ it "should raise an error" do
33
+ expect { ClassInheritable::Array.attach }.to raise_error(ArgumentError)
34
+ end
35
+
36
+ context "with invalid params" do
37
+ it "should raise an error" do
38
+ expect { ClassInheritable::Array.class_eval { ClassInheritable::Array.attach {} } }.to raise_error
39
+ end
40
+ end
41
+ end
42
+
43
+ context "with valid params" do
44
+ it "should not raise an error" do
45
+ expect { some_class.class_eval { ClassInheritable::Array.attach {} } }.not_to raise_error
46
+ expect { ClassInheritable::Array.attach :to => some_class }.not_to raise_error
47
+ end
48
+
49
+ it "should attach expected methods and variables to some class" do
50
+ ( some_class.methods - Object.methods ).should eq( [] )
51
+ ClassInheritable::Array.attach :to => some_class
52
+ ( some_class.methods - Object.methods ).should eq( some_class_expected_methods )
53
+ end
54
+
55
+ context "with params that override attribute and method name(s) on a base class" do
56
+ let(:as) { :required_items }
57
+ let(:writer) { :only_require_item }
58
+ let(:appender) { :require_item }
59
+ let(:method_names) { [ writer, appender, as ] }
60
+ let(:base_class) {
61
+ _as = as; _appender = appender; _writer = writer
62
+ Class.new(Object).tap do |_base_class|
63
+ _base_class.class_eval do
64
+ ClassInheritable::Array.attach(
65
+ :as => _as,
66
+ :appender_name => _appender,
67
+ :writer_name => _writer
68
+ ) {}
69
+ def expected_items
70
+ []
71
+ end
72
+ end
73
+ end
74
+ }
75
+
76
+ it "should add expected attribute and methods to that base class" do
77
+ ( base_class.methods - Object.methods ).should eq( method_names )
78
+ base_class.instance_variables.should == [ "@#{as}".to_sym ]
79
+ end
80
+
81
+ let(:base_class_instance) { base_class.new }
82
+ it "should return expected items from base_class instances" do
83
+ base_class_instance.required_items.should == base_class_instance.expected_items
84
+ end
85
+
86
+
87
+ context "with multiple arrays on a class" do
88
+ let(:subclass_of_base_class) {
89
+ Class.new(base_class).tap do |_subclass_of_base_class|
90
+ _subclass_of_base_class.class_eval do
91
+ ClassInheritable::Array.attach {}
92
+ append_class_inheritable_array [ :cia_1, :cia_2 ]
93
+ require_item :subclass_of_base_class
94
+ end
95
+ end
96
+ }
97
+ it "should maintain both arrays" do
98
+ subclass_of_base_class.required_items.should == [:subclass_of_base_class]
99
+ subclass_of_base_class.class_inheritable_array.should == [:cia_1, :cia_2]
100
+ end
101
+ it "should add new methods to the child but not its parent class" do
102
+ some_class_expected_methods.each do |subclass_method|
103
+ subclass_of_base_class.should respond_to( subclass_method )
104
+ base_class.should_not respond_to( subclass_method )
105
+ end
106
+ end
107
+ end
108
+
109
+ context "given subclasses (of the base class) with appended items" do
110
+ let(:subclass_a) {
111
+ Class.new(base_class).tap do |_subclass_a|
112
+ _subclass_a.class_eval do
113
+ require_item :subclass_a
114
+ def expected_items
115
+ [:subclass_a]
116
+ end
117
+ end
118
+ end
119
+ }
120
+ let(:subclass_a_instance) { subclass_a.new }
121
+
122
+ let(:subclass_b) {
123
+ Class.new(base_class).tap do |_subclass_b|
124
+ _subclass_b.class_eval do
125
+ require_item :subclass_b
126
+ def expected_items
127
+ [:subclass_b]
128
+ end
129
+ end
130
+ end
131
+ }
132
+ let(:subclass_b_instance) { subclass_b.new }
133
+
134
+ it "should return all items in this (sub)class's hierarcy for this array" do
135
+ subclass_a.required_items.should == subclass_a_instance.expected_items
136
+ subclass_b.required_items.should == subclass_b_instance.expected_items
137
+ end
138
+
139
+ it "should return all items in an instance of this (sub)class's hierarcy for this array" do
140
+ subclass_a_instance.required_items.should == subclass_a_instance.expected_items
141
+ subclass_b_instance.required_items.should == subclass_b_instance.expected_items
142
+ end
143
+
144
+ context "given sub-subclasses" do
145
+ context "with overriden items" do
146
+ let(:sub_base_a) {
147
+ Class.new(subclass_a).tap do |_sub_base_a|
148
+ _sub_base_a.class_eval do
149
+ only_require_item :sub_base_a
150
+ def expected_items
151
+ [:sub_base_a]
152
+ end
153
+ end
154
+ end
155
+ }
156
+ let(:sub_base_a_instance) { sub_base_a.new }
157
+
158
+ it "should ignore this (sub)class's parent's items" do
159
+ sub_base_a.required_items.should == sub_base_a_instance.expected_items
160
+ end
161
+
162
+ it "should ignore an instance of this (sub)class's parent's items" do
163
+ sub_base_a_instance.required_items.should == sub_base_a_instance.expected_items
164
+ end
165
+
166
+ context "with a subsequent level of appended items" do
167
+ let(:subclass_sub_base_a) {
168
+ Class.new(sub_base_a).tap do |_subclass_sub_base_a|
169
+ _subclass_sub_base_a.class_eval do
170
+ require_item :subclass_sub_base_a
171
+ def expected_items
172
+ [:sub_base_a, :subclass_sub_base_a]
173
+ end
174
+ end
175
+ end
176
+ }
177
+ let(:subclass_sub_base_a_instance) { subclass_sub_base_a.new }
178
+
179
+ it "should ignore this (sub-sub)class's grand-parent's items" do
180
+ subclass_sub_base_a.required_items.should == subclass_sub_base_a_instance.expected_items
181
+ end
182
+ it "should ignore an instance of this (sub-sub)class's grand-parent's items" do
183
+ subclass_sub_base_a_instance.required_items.should == subclass_sub_base_a_instance.expected_items
184
+ end
185
+ end # overriden-then-extended
186
+ end # overriden
187
+
188
+ context "with more items appended" do
189
+ let(:sub_subclass_b) {
190
+ Class.new(subclass_b).tap do |_sub_subclass_b|
191
+ _sub_subclass_b.class_eval do
192
+ require_item :sub_subclass_b
193
+ def expected_items
194
+ [:subclass_b, :sub_subclass_b]
195
+ end
196
+ end
197
+ end
198
+ }
199
+ let(:sub_subclass_b_instance) { sub_subclass_b.new }
200
+
201
+ it "should include all items in this (sub-sub)class's hierarchy" do
202
+ sub_subclass_b.required_items.should == sub_subclass_b_instance.expected_items
203
+ end
204
+
205
+ it "should include all items in an instance of this (sub-sub)class's hierarchy" do
206
+ sub_subclass_b_instance.required_items.should == sub_subclass_b_instance.expected_items
207
+ end
208
+ end # more appended
209
+ end # sub-subclasses
210
+ end # subclasses
211
+ end # overriden attr/methods
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,5 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require "rspec"
5
+ require "class_inheritable"
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: class_inheritable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - JayTeeSr
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70181116808720 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.11.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70181116808720
25
+ description: Export strings that need to be translated and integrate the completed
26
+ translations
27
+ email:
28
+ - jaytee_sr_at_his-service_dot_net
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ - MIT-LICENSE
37
+ - README.txt
38
+ - Rakefile
39
+ - class_inheritable.gemspec
40
+ - lib/class_inheritable.rb
41
+ - lib/class_inheritable/array.rb
42
+ - lib/class_inheritable/version.rb
43
+ - spec/lib/class_inheritable/array_spec.rb
44
+ - spec/spec_helper.rb
45
+ homepage: https://github.com/JayTeeSF/class_inheritable
46
+ licenses: []
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project: class_inheritable
65
+ rubygems_version: 1.8.17
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Automate management of i18n locale files
69
+ test_files:
70
+ - spec/lib/class_inheritable/array_spec.rb
71
+ - spec/spec_helper.rb