arunthampi-supermodel 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.
- data/MIT-LICENSE +20 -0
- data/README +23 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/lib/supermodel.rb +9 -0
- data/lib/supermodel/base.rb +284 -0
- data/lib/supermodel/errors.rb +6 -0
- data/lib/supermodel/support.rb +2 -0
- data/lib/supermodel/support/extensions.rb +80 -0
- data/lib/supermodel/support/inflections.rb +52 -0
- data/lib/supermodel/support/inflector.rb +277 -0
- data/spec/base/from_json_spec.rb +70 -0
- data/spec/base/has_many_spec.rb +89 -0
- data/spec/base/has_one_spec.rb +80 -0
- data/spec/base/has_spec.rb +88 -0
- data/spec/base/initialize_spec.rb +64 -0
- data/spec/base/marshal_dump_spec.rb +64 -0
- data/spec/base/marshal_load_spec.rb +58 -0
- data/spec/base/module_spec.rb +15 -0
- data/spec/base/nested_class_spec.rb +16 -0
- data/spec/base/to_json_spec.rb +73 -0
- data/spec/spec_helper.rb +9 -0
- data/supermodel.gemspec +15 -0
- metadata +83 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Arun Thampi
|
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
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
SuperModel aims to give sexy semantics to any model/library in Ruby that you might create. This includes has, has_many and has_one semantics which we generally associate with ActiveRecord.
|
2
|
+
|
3
|
+
This project draws its roots from the ActiveCouch project (http://www.github.com/arunthampi/activecouch), after which I saw myself re-using the same semantics in many other projects.
|
4
|
+
|
5
|
+
Hence, the SuperModel project was born!
|
6
|
+
|
7
|
+
The irony is that even though a SuperModel will be more bloated than a regular Ruby model, it will be better looking.
|
8
|
+
|
9
|
+
So with SuperModel, you can define a model such as this:
|
10
|
+
|
11
|
+
class Person < SuperModel::Base
|
12
|
+
has :name, :which_is => :text, :with_default_value => "McLovin"
|
13
|
+
end
|
14
|
+
|
15
|
+
Also supports JSON serialization, so you can do this:
|
16
|
+
|
17
|
+
p = Person.new(:name => 'McLovin').to_json # => {"name":"McLovin"}
|
18
|
+
|
19
|
+
Plans For Future
|
20
|
+
----------------
|
21
|
+
1. Serialization in any format: to_xml, to_yaml methods (and of course from_xml, from_yaml methods as well)
|
22
|
+
2. Callbacks: Define any callback for any event (This is stolen from ActiveRecord)
|
23
|
+
3. More Sexiness and Awesomeness
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
spec = Gem::Specification.new do |s|
|
5
|
+
s.platform = Gem::Platform::RUBY
|
6
|
+
s.summary = "Sexy Semantics for Any Ruby Model"
|
7
|
+
s.name = 'supermodel'
|
8
|
+
s.author = 'Arun Thampi'
|
9
|
+
s.email = "arun.thampi@gmail.com"
|
10
|
+
s.homepage = "http://www.github.com/arunthampi/supermodel"
|
11
|
+
s.version = '0.1.0'
|
12
|
+
s.files = FileList[ '[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb' ],
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.require_path = "lib"
|
15
|
+
s.extra_rdoc_files = ["README"]
|
16
|
+
s.add_dependency 'json', '>=1.1.2'
|
17
|
+
end
|
18
|
+
|
19
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
20
|
+
pkg.need_zip = true
|
21
|
+
pkg.need_tar = true
|
22
|
+
end
|
23
|
+
|
24
|
+
task :lines do
|
25
|
+
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
|
26
|
+
|
27
|
+
for file_name in FileList["lib/supermodel/**/*.rb"]
|
28
|
+
next if file_name =~ /vendor/
|
29
|
+
f = File.open(file_name)
|
30
|
+
|
31
|
+
while line = f.gets
|
32
|
+
lines += 1
|
33
|
+
next if line =~ /^\s*$/
|
34
|
+
next if line =~ /^\s*#/
|
35
|
+
codelines += 1
|
36
|
+
end
|
37
|
+
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
38
|
+
|
39
|
+
total_lines += lines
|
40
|
+
total_codelines += codelines
|
41
|
+
|
42
|
+
lines, codelines = 0, 0
|
43
|
+
end
|
44
|
+
|
45
|
+
puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
|
46
|
+
end
|
47
|
+
|
48
|
+
task :default => [:package]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/supermodel.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
require 'supermodel/support'
|
8
|
+
require 'supermodel/errors'
|
9
|
+
require 'supermodel/base'
|
@@ -0,0 +1,284 @@
|
|
1
|
+
module SuperModel
|
2
|
+
class Base
|
3
|
+
SPECIAL_MEMBERS = %w(attributes has_many_associations has_one_associations)
|
4
|
+
TYPES = { :text => "", :number => 0, :decimal => 0.0, :boolean => true }
|
5
|
+
TYPES.default = ""
|
6
|
+
|
7
|
+
# Initializes an SuperModel::Base object. The constructor accepts both a hash, as well as
|
8
|
+
# a block to initialize attributes
|
9
|
+
#
|
10
|
+
# Examples:
|
11
|
+
# class Person < SuperModel::Base
|
12
|
+
# has :name
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# person1 = Person.new(:name => "McLovin")
|
16
|
+
# person1.name # => "McLovin"
|
17
|
+
#
|
18
|
+
# person2 = Person.new do |p|
|
19
|
+
# p.name = "Seth"
|
20
|
+
# end
|
21
|
+
# person2.name # => "Seth"
|
22
|
+
def initialize(params = {})
|
23
|
+
# Object instance variable
|
24
|
+
@attributes, @has_many_associations = {}, @has_one_associations = {}
|
25
|
+
# Initialize local variables from class instance variables
|
26
|
+
klass_atts = self.class.attributes
|
27
|
+
klass_has_many_assocs = self.class.has_many_associations
|
28
|
+
klass_has_one_assocs = self.class.has_one_associations
|
29
|
+
# Define getter methods for special members
|
30
|
+
SPECIAL_MEMBERS.each do |k|
|
31
|
+
self.instance_eval "def #{k}; @#{k}; end"
|
32
|
+
end
|
33
|
+
# First, initialize all the attributes
|
34
|
+
klass_atts.each_key do |property|
|
35
|
+
@attributes[property] = klass_atts[property]
|
36
|
+
self.instance_eval "def #{property}; attributes[:#{property}]; end"
|
37
|
+
self.instance_eval "def #{property}=(val); attributes[:#{property}] = val; end"
|
38
|
+
end
|
39
|
+
# Then, initialize all the has-many associations
|
40
|
+
klass_has_many_assocs.each do |k,v|
|
41
|
+
@has_many_associations[k] = v
|
42
|
+
self.instance_eval "def #{k}; @#{k} ||= []; end"
|
43
|
+
# If you have has_many :people, this will add a method called add_person
|
44
|
+
# to the object instantiated from the class
|
45
|
+
self.instance_eval "def add_#{k.singularize}(val); @#{k} = #{k} << val; end"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Then, initialize all the has-many associations
|
49
|
+
klass_has_one_assocs.each do |k,v|
|
50
|
+
@has_one_associations[k] = v
|
51
|
+
self.instance_eval "def #{k}; @#{k}; end"
|
52
|
+
# If you have has_many :people, this will add a method called add_person
|
53
|
+
# to the object instantiated from the class
|
54
|
+
self.instance_eval "def #{k.singularize}=(val); @#{k} = val; end"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Set any instance variables if any, which are present in the params hash
|
58
|
+
from_hash(params)
|
59
|
+
# Handle the block, which can also be used to initialize the object
|
60
|
+
yield self if block_given?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generates a JSON representation of an instance of a subclass of SuperModel::Base.
|
64
|
+
# Ignores attributes which have a nil value.
|
65
|
+
#
|
66
|
+
# Examples:
|
67
|
+
# class Person < SuperModel::Base
|
68
|
+
# has :name, :which_is => :text, :with_default_value => "McLovin"
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# person = Person.new
|
72
|
+
# person.to_json # {"name":"McLovin"}
|
73
|
+
#
|
74
|
+
# class AgedPerson < SuperModel::Base
|
75
|
+
# has :age, :which_is => :decimal, :with_default_value => 3.5
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# aged_person = AgedPerson.new
|
79
|
+
# aged_person.id = 'abc-def'
|
80
|
+
# aged_person.to_json # {"age":3.5, "_id":"abc-def"}
|
81
|
+
def to_json
|
82
|
+
hash = {}
|
83
|
+
# First merge the attributes...
|
84
|
+
hash.merge!(attributes.reject{ |k,v| v.nil? })
|
85
|
+
# ...and then the associations, first has_many...
|
86
|
+
has_many_associations.each_key { |name| hash.merge!({ name => self.__send__(name.to_s) }) }
|
87
|
+
# ...and then has_one
|
88
|
+
has_one_associations.each_key { |name| hash.merge!({ name => self.__send__(name.to_s) }) }
|
89
|
+
# and by the Power of Grayskull, convert the hash to json
|
90
|
+
hash.to_json
|
91
|
+
end
|
92
|
+
|
93
|
+
def marshal_dump # :nodoc:
|
94
|
+
# Deflate using Zlib
|
95
|
+
self.to_json
|
96
|
+
end
|
97
|
+
|
98
|
+
def marshal_load(str) # :nodoc:
|
99
|
+
self.instance_eval do
|
100
|
+
# Inflate first, and then parse the JSON
|
101
|
+
hash = JSON.parse(str)
|
102
|
+
initialize(hash)
|
103
|
+
end
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
class << self # Class methods
|
108
|
+
# Defines an attribute for a subclass of SuperModel::Base. The parameters
|
109
|
+
# for this method include name, which is the name of the attribute as well as
|
110
|
+
# an options hash.
|
111
|
+
#
|
112
|
+
# The options hash can contain the key 'which_is' which can
|
113
|
+
# have possible values :text, :decimal, :number. It can also contain the key
|
114
|
+
# 'with_default_value' which can set a default value for each attribute defined
|
115
|
+
# in the subclass of SuperModel::Base
|
116
|
+
#
|
117
|
+
# Examples:
|
118
|
+
# class Person < SuperModel::Base
|
119
|
+
# has :name
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# person = Person.new
|
123
|
+
# p.name.methods.include?(:name) # true
|
124
|
+
# p.name.methods.include?(:name=) # false
|
125
|
+
#
|
126
|
+
# class AgedPerson < SuperModel::Base
|
127
|
+
# has :age, :which_is => :number, :with_default_value = 18
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# person = AgedPerson.new
|
131
|
+
# person.age # 18
|
132
|
+
def has(name, options = {})
|
133
|
+
unless name.is_a?(String) || name.is_a?(Symbol)
|
134
|
+
raise ArgumentError, "#{name} is neither a String nor a Symbol"
|
135
|
+
end
|
136
|
+
# Set the attributes value to options[:with_default_value]
|
137
|
+
# In the constructor, this will be used to initialize the value of
|
138
|
+
# the 'name' instance variable to the value in the hash
|
139
|
+
@attributes[name] = options[:with_default_value] || TYPES[:which_is]
|
140
|
+
end
|
141
|
+
|
142
|
+
# Defines an array of objects which are 'children' of this class. The has_many
|
143
|
+
# function guesses the class of the child, based on the name of the association,
|
144
|
+
# but can be over-ridden by the :class key in the options hash.
|
145
|
+
#
|
146
|
+
# Examples:
|
147
|
+
#
|
148
|
+
# class Person < SuperModel::Base
|
149
|
+
# has :name
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# class GrandPerson < SuperModel::Base
|
153
|
+
# has_many :people # which will create an empty array which can contain
|
154
|
+
# # Person objects
|
155
|
+
# end
|
156
|
+
def has_many(name, options = {})
|
157
|
+
unless name.is_a?(String) || name.is_a?(Symbol)
|
158
|
+
raise ArgumentError, "#{name} is neither a String nor a Symbol"
|
159
|
+
end
|
160
|
+
|
161
|
+
@has_many_associations[name] = get_klass(name, options)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Defines a single object which is a 'child' of this class. The has_one
|
165
|
+
# function guesses the class of the child, based on the name of the association,
|
166
|
+
# but can be over-ridden by the :class key in the options hash.
|
167
|
+
#
|
168
|
+
# Examples:
|
169
|
+
#
|
170
|
+
# class Child < SuperModel::Base
|
171
|
+
# has :name
|
172
|
+
# end
|
173
|
+
#
|
174
|
+
# class GrandParent < SuperModel::Base
|
175
|
+
# has_one :child
|
176
|
+
# end
|
177
|
+
def has_one(name, options = {})
|
178
|
+
unless name.is_a?(String) || name.is_a?(Symbol)
|
179
|
+
raise ArgumentError, "#{name} is neither a String nor a Symbol"
|
180
|
+
end
|
181
|
+
|
182
|
+
@has_one_associations[name] = get_klass(name, options)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Initializes an object of a subclass of SuperModel::Base based on a JSON
|
186
|
+
# representation of the object.
|
187
|
+
#
|
188
|
+
# Example:
|
189
|
+
# class Person < SuperModel::Base
|
190
|
+
# has :name
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# person = Person.from_json('{"name":"McLovin"}')
|
194
|
+
# person.name # "McLovin"
|
195
|
+
def from_json(json)
|
196
|
+
hash = JSON.parse(json)
|
197
|
+
# Create new based on parsed
|
198
|
+
self.new(hash)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Defines an "attribute" method. A new (class) method will be created with the
|
202
|
+
# given name. If a value is specified, the new method will
|
203
|
+
# return that value (as a string). Otherwise, the given block
|
204
|
+
# will be used to compute the value of the method.
|
205
|
+
#
|
206
|
+
# The original method, if it exists, will be aliased, with the
|
207
|
+
# new name being
|
208
|
+
# prefixed with "original_". This allows the new method to
|
209
|
+
# access the original value.
|
210
|
+
#
|
211
|
+
# This method is stolen from ActiveRecord.
|
212
|
+
#
|
213
|
+
# Example:
|
214
|
+
#
|
215
|
+
# class Foo < SuperModel::Base
|
216
|
+
# define_attr_method :database_name, 'foo'
|
217
|
+
# # OR
|
218
|
+
# define_attr_method(:database_name) do
|
219
|
+
# original_database_name + '_legacy'
|
220
|
+
# end
|
221
|
+
# end
|
222
|
+
def define_attr_method(name, value = nil, &block)
|
223
|
+
metaclass.send(:alias_method, "original_#{name}", name)
|
224
|
+
if block_given?
|
225
|
+
meta_def name, &block
|
226
|
+
else
|
227
|
+
metaclass.class_eval "def #{name}; #{value.to_s.inspect}; end"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def inherited(subklass)
|
232
|
+
subklass.instance_eval do
|
233
|
+
@attributes, @has_many_associations, @has_one_associations = {}, {}, {}
|
234
|
+
end
|
235
|
+
|
236
|
+
SPECIAL_MEMBERS.each do |k|
|
237
|
+
subklass.instance_eval "def #{k}; @#{k}; end"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def base_class
|
242
|
+
class_of_supermodel_descendant(self)
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
# Generate a class from a name
|
247
|
+
def get_klass(name, options)
|
248
|
+
klass = options[:class]
|
249
|
+
!klass.nil? && klass.is_a?(Class) ? klass : name.to_s.classify.constantize
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns the class descending directly from SuperModel in the inheritance hierarchy.
|
253
|
+
def class_of_supermodel_descendant(klass)
|
254
|
+
if klass.superclass == Base
|
255
|
+
klass
|
256
|
+
elsif klass.superclass.nil?
|
257
|
+
raise SuperModelError, "#{name} doesn't belong in a hierarchy descending from SuperModel"
|
258
|
+
else
|
259
|
+
class_of_supermodel_descendant(klass.superclass)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end # End class methods
|
263
|
+
|
264
|
+
private
|
265
|
+
def from_hash(hash)
|
266
|
+
hash.each do |property, value|
|
267
|
+
property = property.to_sym rescue property
|
268
|
+
# This means a has_many association
|
269
|
+
if value.is_a?(Array) && !(child_klass = @has_many_associations[property]).nil?
|
270
|
+
value.each do |child|
|
271
|
+
child.is_a?(Hash) ? child_obj = child_klass.new(child) : child_obj = child
|
272
|
+
self.send "add_#{property.to_s.singularize}", child_obj
|
273
|
+
end
|
274
|
+
# This means a has_one association
|
275
|
+
elsif value.is_a?(Hash) && !(child_klass = @has_one_associations[property]).nil?
|
276
|
+
self.send "#{property.to_s.singularize}=", child_klass.new(value)
|
277
|
+
# This means this is a normal attribute
|
278
|
+
else
|
279
|
+
self.send("#{property}=", value) if respond_to?("#{property}=")
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end # End class Base
|
284
|
+
end # End module SuperModel
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module SuperModel
|
2
|
+
|
3
|
+
Symbol.class_eval do
|
4
|
+
def singularize; Inflector.singularize(self); end
|
5
|
+
end
|
6
|
+
|
7
|
+
String.class_eval do
|
8
|
+
require 'cgi'
|
9
|
+
def url_encode; CGI.escape("\"#{self.to_s}\""); end
|
10
|
+
# Delegate to Inflector
|
11
|
+
def singularize; Inflector.singularize(self); end
|
12
|
+
def demodulize; Inflector.demodulize(self); end
|
13
|
+
def pluralize; Inflector.pluralize(self); end
|
14
|
+
def underscore; Inflector.underscore(self); end
|
15
|
+
def classify; Inflector.classify(self); end
|
16
|
+
def constantize; Inflector.constantize(self); end
|
17
|
+
end
|
18
|
+
|
19
|
+
Hash.class_eval do
|
20
|
+
# Flatten on the array removes everything into *one* single array,
|
21
|
+
# so {}.to_a.flatten sometimes won't work nicely because a value might be an array
|
22
|
+
# So..introducing flatten for Hash, so that arrays which are values (to keys)
|
23
|
+
# are retained
|
24
|
+
def flatten
|
25
|
+
(0...self.size).inject([]) {|k,v| k << self.keys[v]; k << self.values[v]}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Object.class_eval do
|
30
|
+
def get_class(name)
|
31
|
+
# From 'The Ruby Way Second Edition' by Hal Fulton
|
32
|
+
# This is to get nested class for e.g. A::B::C
|
33
|
+
name.split("::").inject(Object) {|x,y| x.const_get(y)}
|
34
|
+
end
|
35
|
+
|
36
|
+
# The singleton class.
|
37
|
+
def metaclass; class << self; self; end; end
|
38
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
39
|
+
|
40
|
+
# Adds methods to a metaclass.
|
41
|
+
def meta_def name, &blk
|
42
|
+
meta_eval { define_method name, &blk }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Defines an instance method within a class.
|
46
|
+
def class_def name, &blk
|
47
|
+
class_eval { define_method name, &blk }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Module.module_eval do
|
52
|
+
# Return the module which contains this one; if this is a root module, such as
|
53
|
+
# +::MyModule+, then Object is returned.
|
54
|
+
def parent
|
55
|
+
parent_name = name.split('::')[0..-2] * '::'
|
56
|
+
parent_name.empty? ? Object : Inflector.constantize(parent_name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def alias_method_chain(target, feature)
|
60
|
+
# Strip out punctuation on predicates or bang methods since
|
61
|
+
# e.g. target?_without_feature is not a valid method name.
|
62
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
63
|
+
yield(aliased_target, punctuation) if block_given?
|
64
|
+
|
65
|
+
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
|
66
|
+
|
67
|
+
alias_method without_method, target
|
68
|
+
alias_method target, with_method
|
69
|
+
|
70
|
+
case
|
71
|
+
when public_method_defined?(without_method)
|
72
|
+
public target
|
73
|
+
when protected_method_defined?(without_method)
|
74
|
+
protected target
|
75
|
+
when private_method_defined?(without_method)
|
76
|
+
private target
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|