dao 4.2.1 → 4.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +103 -63
- data/Rakefile +3 -3
- data/dao.gemspec +27 -16
- data/lib/dao.rb +17 -17
- data/lib/dao/active_record.rb +1 -0
- data/lib/dao/api.rb +2 -1
- data/lib/dao/api/{endpoints.rb → call.rb} +1 -0
- data/lib/dao/api/context.rb +2 -0
- data/lib/dao/api/dsl.rb +1 -0
- data/lib/dao/api/initializers.rb +1 -0
- data/lib/dao/api/modes.rb +1 -0
- data/lib/dao/api/routes.rb +1 -0
- data/lib/dao/blankslate.rb +1 -0
- data/lib/dao/conducer.rb +315 -274
- data/lib/dao/conducer/active_model.rb +98 -0
- data/lib/dao/conducer/attributes.rb +1 -0
- data/lib/dao/conducer/autocrud.rb +58 -0
- data/lib/dao/conducer/callback_support.rb +20 -0
- data/lib/dao/conducer/collection.rb +45 -0
- data/lib/dao/conducer/controller_support.rb +104 -0
- data/lib/dao/conducer/nav_support.rb +9 -0
- data/lib/dao/conducer/view_support.rb +16 -0
- data/lib/dao/data.rb +2 -1
- data/lib/dao/db.rb +2 -0
- data/lib/dao/endpoint.rb +1 -0
- data/lib/dao/engine.rb +1 -0
- data/lib/dao/errors.rb +109 -99
- data/lib/dao/exceptions.rb +1 -0
- data/lib/dao/extractor.rb +1 -0
- data/lib/dao/form.rb +175 -20
- data/lib/dao/instance_exec.rb +1 -0
- data/lib/dao/mode.rb +1 -0
- data/lib/dao/mongo_mapper.rb +1 -0
- data/lib/dao/name.rb +1 -0
- data/lib/dao/params.rb +2 -1
- data/lib/dao/path.rb +1 -0
- data/lib/dao/path_map.rb +24 -0
- data/lib/dao/rack.rb +1 -0
- data/lib/dao/rack/middleware.rb +1 -0
- data/lib/dao/rack/middleware/params_parser.rb +1 -0
- data/lib/dao/rails.rb +12 -32
- data/lib/dao/rails/lib/generators/dao/USAGE +2 -2
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +8 -27
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +2 -1
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +22 -20
- data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +49 -43
- data/lib/dao/rails/lib/generators/dao/templates/dao.css +26 -25
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +3 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +58 -45
- data/lib/dao/result.rb +50 -1
- data/lib/dao/route.rb +1 -0
- data/lib/dao/slug.rb +12 -36
- data/lib/dao/status.rb +91 -7
- data/lib/dao/stdext.rb +1 -0
- data/lib/dao/support.rb +90 -80
- data/lib/dao/upload.rb +396 -0
- data/lib/dao/validations.rb +23 -5
- data/lib/dao/validations/callback.rb +5 -0
- data/lib/dao/validations/common.rb +100 -3
- data/lib/dao/validations/instance.rb +17 -0
- data/lib/dao/validations/validator.rb +192 -91
- data/test/active_model_conducer_lint_test.rb +1 -0
- data/test/api_test.rb +15 -0
- data/test/conducer_test.rb +608 -90
- data/test/data/han-solo.jpg +0 -0
- data/test/form_test.rb +1 -0
- data/test/helper.rb +1 -0
- data/test/leak.rb +1 -0
- data/test/support_test.rb +4 -1
- data/test/testing.rb +1 -0
- data/test/validations_test.rb +176 -30
- metadata +120 -131
- data/b.rb +0 -38
- data/lib/dao/conducer/crud.rb +0 -70
- data/lib/dao/current.rb +0 -66
- data/lib/dao/image_cache.rb +0 -193
- data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +0 -79
- data/test/db.yml +0 -9
data/lib/dao/result.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Dao
|
2
3
|
class Result < ::Map
|
3
4
|
def initialize(*args, &block)
|
@@ -12,8 +13,56 @@ module Dao
|
|
12
13
|
self.data = options[:data] || Data.new
|
13
14
|
end
|
14
15
|
|
16
|
+
=begin
|
17
|
+
%w(
|
18
|
+
path
|
19
|
+
route
|
20
|
+
mode
|
21
|
+
status
|
22
|
+
params
|
23
|
+
errors
|
24
|
+
data
|
25
|
+
).each do |attr|
|
26
|
+
|
27
|
+
module_eval <<-__, __FILE__, __LINE__
|
28
|
+
def #{ attr }(*value)
|
29
|
+
unless value.empty?
|
30
|
+
self["#{ attr }"] = value.first
|
31
|
+
end
|
32
|
+
self["#{ attr }"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def #{ attr }=(value)
|
36
|
+
self["#{ attr }"] = value
|
37
|
+
end
|
38
|
+
__
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def name
|
43
|
+
path
|
44
|
+
end
|
45
|
+
|
46
|
+
def attributes
|
47
|
+
params
|
48
|
+
end
|
49
|
+
=end
|
50
|
+
|
51
|
+
|
52
|
+
def form
|
53
|
+
@form ||= (
|
54
|
+
Form.new.tap do |f|
|
55
|
+
f.object = self
|
56
|
+
f.attributes = params
|
57
|
+
f.errors = errors
|
58
|
+
f.status = status
|
59
|
+
f.name = path
|
60
|
+
end
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
15
64
|
def inspect
|
16
|
-
|
65
|
+
Dao.json_for(self)
|
17
66
|
end
|
18
67
|
end
|
19
68
|
end
|
data/lib/dao/route.rb
CHANGED
data/lib/dao/slug.rb
CHANGED
@@ -1,40 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'unidecode'
|
8
|
-
rescue LoadError
|
9
|
-
end
|
10
|
-
|
11
|
-
class Slug < ::String
|
12
|
-
Slug::Version = '0.0.1'
|
13
|
-
|
14
|
-
def Slug.version
|
15
|
-
'0.0.1'
|
16
|
-
end
|
17
|
-
|
18
|
-
Join = '-'
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Dao
|
3
|
+
class Slug < ::String
|
4
|
+
Join = '-'
|
19
5
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
new(words.join(join).downcase)
|
29
|
-
end
|
30
|
-
|
31
|
-
unless defined?(Unidecoder)
|
32
|
-
def Slug.unidecode(string)
|
33
|
-
string
|
34
|
-
end
|
35
|
-
else
|
36
|
-
def Slug.unidecode(string)
|
37
|
-
Unidecoder.decode(string)
|
6
|
+
def Slug.for(*args)
|
7
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
8
|
+
join = options[:join]||options['join']||Join
|
9
|
+
string = args.flatten.compact.join(join)
|
10
|
+
words = string.to_s.scan(%r/\w+/)
|
11
|
+
words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
|
12
|
+
words.delete_if{|word| word.nil? or word.strip.empty?}
|
13
|
+
new(words.join(join).downcase)
|
38
14
|
end
|
39
15
|
end
|
40
16
|
end
|
data/lib/dao/status.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Dao
|
2
3
|
class Status < ::String
|
3
4
|
## http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
@@ -57,7 +58,84 @@ module Dao
|
|
57
58
|
504 => "Gateway Timeout",
|
58
59
|
505 => "HTTP Version Not Supported",
|
59
60
|
507 => "Insufficient Storage",
|
60
|
-
510 => "Not Extended"
|
61
|
+
510 => "Not Extended",
|
62
|
+
|
63
|
+
|
64
|
+
### ref: https://github.com/joho/7XX-rfc
|
65
|
+
|
66
|
+
#70X => "Inexcusable",
|
67
|
+
701 => "Meh",
|
68
|
+
702 => "Emacs",
|
69
|
+
|
70
|
+
#71X => "Novelty Implementations",
|
71
|
+
710 => "PHP",
|
72
|
+
711 => "Convenience Store",
|
73
|
+
719 => "I am not a teapot",
|
74
|
+
|
75
|
+
#72X => "Edge Cases",
|
76
|
+
720 => "Unpossible",
|
77
|
+
721 => "Known Unknowns",
|
78
|
+
722 => "Unknown Unknowns",
|
79
|
+
723 => "Tricky",
|
80
|
+
724 => "This line should be unreachable",
|
81
|
+
725 => "It works on my machine",
|
82
|
+
726 => "It's a feature, not a bug",
|
83
|
+
|
84
|
+
#73X => "Fucking",
|
85
|
+
731 => "Fucking Rubygems",
|
86
|
+
732 => "Fucking Unicode",
|
87
|
+
733 => "Fucking Deadlocks",
|
88
|
+
734 => "Fucking Deferreds",
|
89
|
+
735 => "Fucking IE",
|
90
|
+
736 => "Fucking Race Conditions",
|
91
|
+
737 => "FuckThreadsing",
|
92
|
+
738 => "Fucking Bundler",
|
93
|
+
739 => "Fucking Windows",
|
94
|
+
|
95
|
+
#74X => "Meme Driven",
|
96
|
+
741 => "Compiling",
|
97
|
+
742 => "A kitten dies",
|
98
|
+
743 => "I thought I knew regular expressions",
|
99
|
+
744 => "Y U NO write integration tests?",
|
100
|
+
745 => "I don't always test my code, but when I do I do it in production",
|
101
|
+
746 => "Missed Ballmer Peak",
|
102
|
+
747 => "Motherfucking Snakes on the Motherfucking Plane",
|
103
|
+
748 => "Confounded by Ponies",
|
104
|
+
749 => "Reserved for Chuck Norris",
|
105
|
+
|
106
|
+
#75X => "Syntax Errors",
|
107
|
+
750 => "Didn't bother to compile it",
|
108
|
+
753 => "Syntax Error",
|
109
|
+
|
110
|
+
#76X => "Substance-Affected Developer",
|
111
|
+
761 => "Hungover",
|
112
|
+
762 => "Stoned",
|
113
|
+
763 => "Under-Caffeinated",
|
114
|
+
764 => "Over-Caffeinated",
|
115
|
+
765 => "Railscamp",
|
116
|
+
766 => "Sober",
|
117
|
+
767 => "Drunk",
|
118
|
+
|
119
|
+
#77X => "Predictable Problems",
|
120
|
+
771 => "Cached for too long",
|
121
|
+
772 => "Not cached long enough",
|
122
|
+
773 => "Not cached at all",
|
123
|
+
774 => "Why was this cached?",
|
124
|
+
776 => "Error on the Exception",
|
125
|
+
777 => "Coincidence",
|
126
|
+
778 => "Off By One Error",
|
127
|
+
779 => "Off By Too Many To Count Error",
|
128
|
+
|
129
|
+
#78X => "Somebody Else's Problem",
|
130
|
+
781 => "Operations",
|
131
|
+
782 => "QA",
|
132
|
+
783 => "It was a customer request, honestly",
|
133
|
+
784 => "Management, obviously",
|
134
|
+
785 => "TPS Cover Sheet not attached",
|
135
|
+
|
136
|
+
#79X => "Internet crashed",
|
137
|
+
797 => "This is the last page of the Internet. Go back",
|
138
|
+
799 => "End of the world"
|
61
139
|
}
|
62
140
|
) unless defined?(Code2Message)
|
63
141
|
|
@@ -66,7 +144,8 @@ module Dao
|
|
66
144
|
200 => 'success',
|
67
145
|
300 => 'redirection',
|
68
146
|
400 => 'client_error',
|
69
|
-
500 => 'server_error'
|
147
|
+
500 => 'server_error',
|
148
|
+
700 => 'developer_error'
|
70
149
|
}) unless defined?(Groups)
|
71
150
|
|
72
151
|
# class methods
|
@@ -78,10 +157,14 @@ module Dao
|
|
78
157
|
|
79
158
|
def underscore(camel_cased_word)
|
80
159
|
camel_cased_word.to_s.gsub(/::/, '/').
|
160
|
+
gsub(/\s+/, '_').
|
81
161
|
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
82
162
|
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
83
|
-
|
163
|
+
gsub(/[^A-Za-z0-9_]/, '_').
|
84
164
|
tr("-", "_").
|
165
|
+
squeeze('_').
|
166
|
+
gsub(/^_+/, '').
|
167
|
+
gsub(/_+$/, '').
|
85
168
|
downcase
|
86
169
|
end
|
87
170
|
|
@@ -150,7 +233,7 @@ module Dao
|
|
150
233
|
|
151
234
|
Symbol2Code = (
|
152
235
|
Code2Message.inject(Hash.new) do |hash, (code, message)|
|
153
|
-
sym = Status.underscore(message.gsub(/\s+/, "")).to_sym
|
236
|
+
sym = Status.underscore(message.gsub(/\s+/, "_")).to_sym
|
154
237
|
hash.update(sym => code)
|
155
238
|
end
|
156
239
|
) unless defined?(Symbol2Code)
|
@@ -165,9 +248,10 @@ module Dao
|
|
165
248
|
|
166
249
|
# instance methods
|
167
250
|
#
|
168
|
-
|
169
|
-
|
170
|
-
|
251
|
+
attr_accessor :code
|
252
|
+
attr_accessor :message
|
253
|
+
attr_accessor :group
|
254
|
+
attr_accessor :source
|
171
255
|
|
172
256
|
def initialize(*args)
|
173
257
|
update(*args)
|
data/lib/dao/stdext.rb
CHANGED
data/lib/dao/support.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Dao
|
2
3
|
def map_for(*args, &block)
|
3
4
|
Map.for(*args, &block)
|
@@ -66,42 +67,29 @@ module Dao
|
|
66
67
|
Form.name_for(path, *keys)
|
67
68
|
end
|
68
69
|
|
69
|
-
def parse(*args, &block)
|
70
|
-
Params.process(*args, &block)
|
71
|
-
end
|
72
|
-
|
73
|
-
def normalize_parameters(params)
|
74
|
-
Params.normalize_parameters(params)
|
75
|
-
end
|
76
|
-
|
77
70
|
def current
|
78
|
-
|
79
|
-
Map.new(
|
80
|
-
:controller => nil
|
81
|
-
)
|
71
|
+
Current
|
82
72
|
end
|
83
73
|
|
84
74
|
def current_controller(*args)
|
85
|
-
|
86
|
-
|
75
|
+
Current.controller = args.first unless args.empty?
|
76
|
+
Current.controller
|
87
77
|
end
|
88
78
|
alias_method('controller', 'current_controller')
|
89
79
|
|
90
80
|
def current_controller=(controller)
|
91
|
-
|
81
|
+
Current.controller = controller
|
92
82
|
end
|
93
83
|
alias_method('controller=', 'current_controller=')
|
94
84
|
|
85
|
+
def mock_controller
|
86
|
+
Current.mock_controller
|
87
|
+
end
|
88
|
+
|
95
89
|
%w( request response session ).each do |attr|
|
96
90
|
module_eval <<-__, __FILE__, __LINE__
|
97
91
|
def current_#{ attr }
|
98
|
-
|
99
|
-
end
|
100
|
-
def current_#{ attr }=(value)
|
101
|
-
@current_#{ attr } = value
|
102
|
-
end
|
103
|
-
def #{ attr }
|
104
|
-
current_#{ attr }
|
92
|
+
current_controller.instance_eval{ #{ attr } }
|
105
93
|
end
|
106
94
|
__
|
107
95
|
end
|
@@ -109,7 +97,7 @@ module Dao
|
|
109
97
|
%w( current_user effective_user real_user ).each do |attr|
|
110
98
|
module_eval <<-__, __FILE__, __LINE__
|
111
99
|
def #{ attr }
|
112
|
-
|
100
|
+
current_controller.instance_eval{ #{ attr } }
|
113
101
|
end
|
114
102
|
__
|
115
103
|
end
|
@@ -122,58 +110,13 @@ module Dao
|
|
122
110
|
end
|
123
111
|
end
|
124
112
|
|
125
|
-
def key_for(*keys)
|
126
|
-
key = keys.flatten.join('.').strip
|
127
|
-
key.split(%r/\s*[,.:_-]\s*/).map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
|
128
|
-
end
|
129
|
-
|
130
|
-
def mock_controller
|
131
|
-
ensure_rails_application do
|
132
|
-
require 'action_dispatch/testing/test_request.rb'
|
133
|
-
require 'action_dispatch/testing/test_response.rb'
|
134
|
-
store = ActiveSupport::Cache::MemoryStore.new
|
135
|
-
controller =
|
136
|
-
begin
|
137
|
-
ApplicationController.new
|
138
|
-
rescue NameError
|
139
|
-
ActionController::Base.new
|
140
|
-
end
|
141
|
-
controller.perform_caching = true
|
142
|
-
controller.cache_store = store
|
143
|
-
request = ActionDispatch::TestRequest.new
|
144
|
-
response = ActionDispatch::TestResponse.new
|
145
|
-
controller.request = request
|
146
|
-
controller.response = response
|
147
|
-
controller.send(:initialize_template_class, response)
|
148
|
-
controller.send(:assign_shortcuts, request, response)
|
149
|
-
controller.send(:default_url_options).merge!(DefaultUrlOptions) if defined?(DefaultUrlOptions)
|
150
|
-
controller
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def ensure_rails_application(&block)
|
155
|
-
if Rails.application.nil?
|
156
|
-
mock = Class.new(Rails::Application)
|
157
|
-
Rails.application = mock.instance
|
158
|
-
begin
|
159
|
-
block.call()
|
160
|
-
ensure
|
161
|
-
Rails.application = nil
|
162
|
-
end
|
163
|
-
else
|
164
|
-
block.call()
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
113
|
def normalize_parameters(params)
|
169
114
|
dao = (params.delete('dao') || {}).merge(params.delete(:dao) || {})
|
170
115
|
|
171
|
-
unless dao.
|
116
|
+
unless dao.empty?
|
172
117
|
dao.each do |key, paths_and_values|
|
173
|
-
params[key] = nil
|
174
118
|
next if paths_and_values.blank?
|
175
|
-
|
176
|
-
map = Map.new
|
119
|
+
map = Map.for(params[key])
|
177
120
|
|
178
121
|
paths_and_values.each do |path, value|
|
179
122
|
keys = keys_for(path)
|
@@ -182,16 +125,25 @@ module Dao
|
|
182
125
|
|
183
126
|
params[key] = map
|
184
127
|
end
|
128
|
+
|
129
|
+
params['dao'] = dao
|
185
130
|
end
|
186
131
|
|
187
|
-
#params[:dao] = {:normalized => true}
|
188
132
|
params
|
189
133
|
end
|
190
134
|
|
191
|
-
def keys_for(keys)
|
192
|
-
keys.
|
135
|
+
def keys_for(*keys)
|
136
|
+
keys = keys.join('.').scan(/[^\,\.\s]+/iomx)
|
137
|
+
|
138
|
+
keys.map do |key|
|
139
|
+
digity, stringy, digits = %r/^(~)?(\d+)$/iomx.match(key).to_a
|
140
|
+
|
141
|
+
digity ? stringy ? String(digits) : Integer(digits) : key
|
142
|
+
end
|
193
143
|
end
|
194
144
|
|
145
|
+
alias_method(:key_for, :keys_for)
|
146
|
+
|
195
147
|
def render_json(object, options = {})
|
196
148
|
options = options.to_options!
|
197
149
|
controller = options[:controller] || Dao.current_controller
|
@@ -210,18 +162,76 @@ module Dao
|
|
210
162
|
end
|
211
163
|
end
|
212
164
|
|
213
|
-
def json_for(object)
|
165
|
+
def json_for(object, options = {})
|
214
166
|
object = object.as_json if object.respond_to?(:as_json)
|
215
167
|
|
168
|
+
options = options.empty? ? Map.for(options) : options
|
169
|
+
options[:pretty] = json_pretty? unless options.has_key?(:pretty)
|
170
|
+
|
216
171
|
begin
|
217
|
-
|
218
|
-
::JSON.generate(object)
|
219
|
-
else
|
220
|
-
::JSON.pretty_generate(object, :max_nesting => 0)
|
221
|
-
end
|
172
|
+
MultiJson.dump(object, options)
|
222
173
|
rescue Object => e
|
223
|
-
Rails.logger.error(e)
|
224
174
|
YAML.load( object.to_yaml ).to_json
|
225
175
|
end
|
226
176
|
end
|
177
|
+
|
178
|
+
def json_pretty?
|
179
|
+
@json_pretty ||= (defined?(Rails) ? !Rails.env.production? : true)
|
180
|
+
end
|
181
|
+
|
182
|
+
def call(object, method, *args, &block)
|
183
|
+
args = Dao.args_for_arity(args, object.method(method).arity)
|
184
|
+
object.send(method, *args, &block)
|
185
|
+
end
|
186
|
+
|
187
|
+
def args_for_arity(args, arity)
|
188
|
+
arity = Integer(arity.respond_to?(:arity) ? arity.arity : arity)
|
189
|
+
arity < 0 ? args.dup : args.slice(0, arity)
|
190
|
+
end
|
191
|
+
|
192
|
+
def tree_walk(node, *path, &block)
|
193
|
+
iterator = Array === node ? :each_with_index : :each
|
194
|
+
|
195
|
+
node.send(iterator) do |key, val|
|
196
|
+
key, val = val, key if Array === node
|
197
|
+
path.push(key)
|
198
|
+
begin
|
199
|
+
caught =
|
200
|
+
catch(:tree_walk) do
|
201
|
+
block.call(path, val)
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
next if caught==:next_sibling
|
205
|
+
|
206
|
+
case val
|
207
|
+
when Hash, Array
|
208
|
+
tree_walk(val, *path, &block)
|
209
|
+
end
|
210
|
+
ensure
|
211
|
+
path.pop
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
{
|
218
|
+
'ffi-uuid' => proc{|*args| FFI::UUID.generate_time.to_s},
|
219
|
+
'uuidtools' => proc{|*args| UUIDTools::UUID.timestamp_create.to_s},
|
220
|
+
'uuid' => proc{|*args| UUID.generate.to_s},
|
221
|
+
}.each do |lib, implementation|
|
222
|
+
begin
|
223
|
+
require(lib)
|
224
|
+
define_method(:uuid, &implementation)
|
225
|
+
break
|
226
|
+
rescue LoadError
|
227
|
+
nil
|
228
|
+
end
|
229
|
+
end
|
230
|
+
abort 'no suitable uuid generation library detected' unless method_defined?(:uuid)
|
231
|
+
|
232
|
+
def ensure_interface!(object, *interface)
|
233
|
+
interface.flatten.compact.each do |method|
|
234
|
+
raise(NotImplementedError, "#{ object.class.name }##{ method }") unless object.respond_to?(method)
|
235
|
+
end
|
236
|
+
end
|
227
237
|
end
|