rubyjedi-actionwebservice 2.3.5.20100615120735
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/CHANGELOG +335 -0
- data/MIT-LICENSE +21 -0
- data/README +381 -0
- data/Rakefile +180 -0
- data/TODO +32 -0
- data/examples/googlesearch/README +143 -0
- data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
- data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
- data/examples/googlesearch/delegated/google_search_service.rb +108 -0
- data/examples/googlesearch/delegated/search_controller.rb +7 -0
- data/examples/googlesearch/direct/google_search_api.rb +50 -0
- data/examples/googlesearch/direct/search_controller.rb +58 -0
- data/examples/metaWeblog/README +17 -0
- data/examples/metaWeblog/apis/blogger_api.rb +60 -0
- data/examples/metaWeblog/apis/blogger_service.rb +34 -0
- data/examples/metaWeblog/apis/meta_weblog_api.rb +67 -0
- data/examples/metaWeblog/apis/meta_weblog_service.rb +48 -0
- data/examples/metaWeblog/controllers/xmlrpc_controller.rb +16 -0
- data/generators/web_service/USAGE +28 -0
- data/generators/web_service/templates/api_definition.rb +5 -0
- data/generators/web_service/templates/controller.rb +8 -0
- data/generators/web_service/templates/functional_test.rb +19 -0
- data/generators/web_service/web_service_generator.rb +29 -0
- data/lib/action_web_service/acts_as_web_service.rb +24 -0
- data/lib/action_web_service/api.rb +297 -0
- data/lib/action_web_service/base.rb +38 -0
- data/lib/action_web_service/casting.rb +151 -0
- data/lib/action_web_service/client/base.rb +28 -0
- data/lib/action_web_service/client/soap_client.rb +113 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
- data/lib/action_web_service/client.rb +3 -0
- data/lib/action_web_service/container/action_controller_container.rb +93 -0
- data/lib/action_web_service/container/delegated_container.rb +86 -0
- data/lib/action_web_service/container/direct_container.rb +69 -0
- data/lib/action_web_service/container.rb +3 -0
- data/lib/action_web_service/dispatcher/abstract.rb +208 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +396 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/invocation.rb +202 -0
- data/lib/action_web_service/protocol/abstract.rb +112 -0
- data/lib/action_web_service/protocol/discovery.rb +37 -0
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +123 -0
- data/lib/action_web_service/protocol.rb +4 -0
- data/lib/action_web_service/scaffolding.rb +281 -0
- data/lib/action_web_service/simple.rb +53 -0
- data/lib/action_web_service/string_to_datetime_for_soap.rb +16 -0
- data/lib/action_web_service/struct.rb +68 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_web_service/support/signature_types.rb +261 -0
- data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
- data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
- data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
- data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
- data/lib/action_web_service/test_invoke.rb +110 -0
- data/lib/action_web_service/version.rb +9 -0
- data/lib/action_web_service.rb +60 -0
- data/lib/actionwebservice.rb +1 -0
- data/setup.rb +1379 -0
- data/test/abstract_client.rb +184 -0
- data/test/abstract_dispatcher.rb +549 -0
- data/test/abstract_unit.rb +43 -0
- data/test/actionwebservice_unittest.db +0 -0
- data/test/api_test.rb +102 -0
- data/test/apis/auto_load_api.rb +3 -0
- data/test/apis/broken_auto_load_api.rb +2 -0
- data/test/base_test.rb +42 -0
- data/test/casting_test.rb +95 -0
- data/test/client_soap_test.rb +156 -0
- data/test/client_xmlrpc_test.rb +154 -0
- data/test/container_test.rb +75 -0
- data/test/debug.log +12305 -0
- data/test/dispatcher_action_controller_soap_test.rb +139 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
- data/test/fixtures/db_definitions/mysql.sql +8 -0
- data/test/fixtures/db_definitions/sqlite3.sql +8 -0
- data/test/fixtures/users.yml +12 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +186 -0
- data/test/run +6 -0
- data/test/scaffolded_controller_test.rb +147 -0
- data/test/struct_test.rb +84 -0
- data/test/test_invoke_test.rb +113 -0
- metadata +182 -0
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module ActionWebService
|
5
|
+
module Scaffolding # :nodoc:
|
6
|
+
class ScaffoldingError < ActionWebServiceError # :nodoc:
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Web service invocation scaffolding provides a way to quickly invoke web service methods in a controller. The
|
14
|
+
# generated scaffold actions have default views to let you enter the method parameters and view the
|
15
|
+
# results.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
#
|
19
|
+
# class ApiController < ActionController
|
20
|
+
# web_service_scaffold :invoke
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# This example generates an +invoke+ action in the +ApiController+ that you can navigate to from
|
24
|
+
# your browser, select the API method, enter its parameters, and perform the invocation.
|
25
|
+
#
|
26
|
+
# If you want to customize the default views, create the following views in "app/views":
|
27
|
+
#
|
28
|
+
# * <tt>action_name/methods.html.erb</tt>
|
29
|
+
# * <tt>action_name/parameters.html.erb</tt>
|
30
|
+
# * <tt>action_name/result.html.erb</tt>
|
31
|
+
# * <tt>action_name/layout.html.erb</tt>
|
32
|
+
#
|
33
|
+
# Where <tt>action_name</tt> is the name of the action you gave to ClassMethods#web_service_scaffold.
|
34
|
+
#
|
35
|
+
# You can use the default views in <tt>RAILS_DIR/lib/action_web_service/templates/scaffolds</tt> as
|
36
|
+
# a guide.
|
37
|
+
module ClassMethods
|
38
|
+
# Generates web service invocation scaffolding for the current controller. The given action name
|
39
|
+
# can then be used as the entry point for invoking API methods from a web browser.
|
40
|
+
def web_service_scaffold(action_name)
|
41
|
+
add_template_helper(Helpers)
|
42
|
+
module_eval <<-"end_eval", __FILE__, __LINE__ + 1
|
43
|
+
def #{action_name}
|
44
|
+
if request.method == :get
|
45
|
+
setup_invocation_assigns
|
46
|
+
render_invocation_scaffold 'methods'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def #{action_name}_method_params
|
51
|
+
if request.method == :get
|
52
|
+
setup_invocation_assigns
|
53
|
+
render_invocation_scaffold 'parameters'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def #{action_name}_submit
|
58
|
+
if request.method == :post
|
59
|
+
setup_invocation_assigns
|
60
|
+
protocol_name = params['protocol'] ? params['protocol'].to_sym : :soap
|
61
|
+
case protocol_name
|
62
|
+
when :soap
|
63
|
+
@protocol = Protocol::Soap::SoapProtocol.create(self)
|
64
|
+
when :xmlrpc
|
65
|
+
@protocol = Protocol::XmlRpc::XmlRpcProtocol.create(self)
|
66
|
+
end
|
67
|
+
bm = Benchmark.measure do
|
68
|
+
@protocol.register_api(@scaffold_service.api)
|
69
|
+
post_params = params['method_params'] ? params['method_params'].dup : nil
|
70
|
+
params = []
|
71
|
+
@scaffold_method.expects.each_with_index do |spec, i|
|
72
|
+
params << post_params[i.to_s]
|
73
|
+
end if @scaffold_method.expects
|
74
|
+
params = @scaffold_method.cast_expects(params)
|
75
|
+
method_name = public_method_name(@scaffold_service.name, @scaffold_method.public_name)
|
76
|
+
@method_request_xml = @protocol.encode_request(method_name, params, @scaffold_method.expects)
|
77
|
+
new_request = @protocol.encode_action_pack_request(@scaffold_service.name, @scaffold_method.public_name, @method_request_xml)
|
78
|
+
prepare_request(new_request, @scaffold_service.name, @scaffold_method.public_name)
|
79
|
+
self.request = new_request
|
80
|
+
if @scaffold_container.dispatching_mode != :direct
|
81
|
+
request.parameters['action'] = @scaffold_service.name
|
82
|
+
end
|
83
|
+
dispatch_web_service_request
|
84
|
+
@method_response_xml = response.body
|
85
|
+
method_name, obj = @protocol.decode_response(@method_response_xml)
|
86
|
+
return if handle_invocation_exception(obj)
|
87
|
+
@method_return_value = @scaffold_method.cast_returns(obj)
|
88
|
+
end
|
89
|
+
@method_elapsed = bm.real
|
90
|
+
reset_invocation_response
|
91
|
+
render_invocation_scaffold 'result'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def setup_invocation_assigns
|
97
|
+
@scaffold_class = self.class
|
98
|
+
@scaffold_action_name = "#{action_name}"
|
99
|
+
@scaffold_container = WebServiceModel::Container.new(self)
|
100
|
+
if params['service'] && params['method']
|
101
|
+
@scaffold_service = @scaffold_container.services.find{ |x| x.name == params['service'] }
|
102
|
+
@scaffold_method = @scaffold_service.api_methods[params['method']]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def render_invocation_scaffold(action)
|
107
|
+
customized_template = "\#{self.class.controller_path}/#{action_name}/\#{action}"
|
108
|
+
default_template = scaffold_path(action)
|
109
|
+
begin
|
110
|
+
content = @template.render(:file => customized_template)
|
111
|
+
rescue ActionView::MissingTemplate
|
112
|
+
content = @template.render(:file => default_template)
|
113
|
+
end
|
114
|
+
@template.instance_variable_set("@content_for_layout", content)
|
115
|
+
if self.active_layout.nil?
|
116
|
+
render :file => scaffold_path("layout")
|
117
|
+
else
|
118
|
+
render :file => self.active_layout, :use_full_path => true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def scaffold_path(template_name)
|
123
|
+
File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".html.erb"
|
124
|
+
end
|
125
|
+
|
126
|
+
def reset_invocation_response
|
127
|
+
erase_render_results
|
128
|
+
response.instance_variable_set :@header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
|
129
|
+
end
|
130
|
+
|
131
|
+
def public_method_name(service_name, method_name)
|
132
|
+
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::XmlRpc::XmlRpcProtocol)
|
133
|
+
service_name + '.' + method_name
|
134
|
+
else
|
135
|
+
method_name
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def prepare_request(new_request, service_name, method_name)
|
140
|
+
new_request.parameters.update(request.parameters)
|
141
|
+
request.env.each{ |k, v| new_request.env[k] = v unless new_request.env.has_key?(k) }
|
142
|
+
if web_service_dispatching_mode == :layered && @protocol.is_a?(ActionWebService::Protocol::Soap::SoapProtocol)
|
143
|
+
new_request.env['HTTP_SOAPACTION'] = "/\#{controller_name()}/\#{service_name}/\#{method_name}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def handle_invocation_exception(obj)
|
148
|
+
exception = nil
|
149
|
+
if obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && obj.detail.cause.is_a?(Exception)
|
150
|
+
exception = obj.detail.cause
|
151
|
+
elsif obj.is_a?(XMLRPC::FaultException)
|
152
|
+
exception = obj
|
153
|
+
end
|
154
|
+
return unless exception
|
155
|
+
reset_invocation_response
|
156
|
+
rescue_action(exception)
|
157
|
+
true
|
158
|
+
end
|
159
|
+
end_eval
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
module Helpers # :nodoc:
|
164
|
+
def method_parameter_input_fields(method, type, field_name_base, idx, was_structured=false)
|
165
|
+
if type.array?
|
166
|
+
return content_tag('em', "Typed array input fields not supported yet (#{type.name})")
|
167
|
+
end
|
168
|
+
if type.structured?
|
169
|
+
return content_tag('em', "Nested structural types not supported yet (#{type.name})") if was_structured
|
170
|
+
parameters = ""
|
171
|
+
type.each_member do |member_name, member_type|
|
172
|
+
label = method_parameter_label(member_name, member_type)
|
173
|
+
nested_content = method_parameter_input_fields(
|
174
|
+
method,
|
175
|
+
member_type,
|
176
|
+
"#{field_name_base}[#{idx}][#{member_name}]",
|
177
|
+
idx,
|
178
|
+
true)
|
179
|
+
if member_type.custom?
|
180
|
+
parameters << content_tag('li', label)
|
181
|
+
parameters << content_tag('ul', nested_content)
|
182
|
+
else
|
183
|
+
parameters << content_tag('li', label + ' ' + nested_content)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
content_tag('ul', parameters)
|
187
|
+
else
|
188
|
+
# If the data source was structured previously we already have the index set
|
189
|
+
field_name_base = "#{field_name_base}[#{idx}]" unless was_structured
|
190
|
+
|
191
|
+
case type.type
|
192
|
+
when :int
|
193
|
+
text_field_tag "#{field_name_base}"
|
194
|
+
when :string
|
195
|
+
text_field_tag "#{field_name_base}"
|
196
|
+
when :base64
|
197
|
+
text_area_tag "#{field_name_base}", nil, :size => "40x5"
|
198
|
+
when :bool
|
199
|
+
radio_button_tag("#{field_name_base}", "true") + " True" +
|
200
|
+
radio_button_tag("#{field_name_base}", "false") + "False"
|
201
|
+
when :float
|
202
|
+
text_field_tag "#{field_name_base}"
|
203
|
+
when :time, :datetime
|
204
|
+
time = Time.now
|
205
|
+
i = 0
|
206
|
+
%w|year month day hour minute second|.map do |name|
|
207
|
+
i += 1
|
208
|
+
send("select_#{name}", time, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
|
209
|
+
end.join
|
210
|
+
when :date
|
211
|
+
date = Date.today
|
212
|
+
i = 0
|
213
|
+
%w|year month day|.map do |name|
|
214
|
+
i += 1
|
215
|
+
send("select_#{name}", date, :prefix => "#{field_name_base}[#{i}]", :discard_type => true)
|
216
|
+
end.join
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def method_parameter_label(name, type)
|
222
|
+
name.to_s.capitalize + ' (' + type.human_name(false) + ')'
|
223
|
+
end
|
224
|
+
|
225
|
+
def service_method_list(service)
|
226
|
+
action = @scaffold_action_name + '_method_params'
|
227
|
+
methods = service.api_methods_full.sort {|a, b| a[1] <=> b[1]}.map do |desc, name|
|
228
|
+
content_tag("li", link_to(name, :action => action, :service => service.name, :method => name))
|
229
|
+
end
|
230
|
+
content_tag("ul", methods.join("\n"))
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
module WebServiceModel # :nodoc:
|
235
|
+
class Container # :nodoc:
|
236
|
+
attr :services
|
237
|
+
attr :dispatching_mode
|
238
|
+
|
239
|
+
def initialize(real_container)
|
240
|
+
@real_container = real_container
|
241
|
+
@dispatching_mode = @real_container.class.web_service_dispatching_mode
|
242
|
+
@services = []
|
243
|
+
if @dispatching_mode == :direct
|
244
|
+
@services << Service.new(@real_container.controller_name, @real_container)
|
245
|
+
else
|
246
|
+
@real_container.class.web_services.each do |name, obj|
|
247
|
+
@services << Service.new(name, @real_container.instance_eval{ web_service_object(name) })
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
class Service # :nodoc:
|
254
|
+
attr :name
|
255
|
+
attr :object
|
256
|
+
attr :api
|
257
|
+
attr :api_methods
|
258
|
+
attr :api_methods_full
|
259
|
+
|
260
|
+
def initialize(name, real_service)
|
261
|
+
@name = name.to_s
|
262
|
+
@object = real_service
|
263
|
+
@api = @object.class.web_service_api
|
264
|
+
if @api.nil?
|
265
|
+
raise ScaffoldingError, "No web service API attached to #{object.class}"
|
266
|
+
end
|
267
|
+
@api_methods = {}
|
268
|
+
@api_methods_full = []
|
269
|
+
@api.api_methods.each do |name, method|
|
270
|
+
@api_methods[method.public_name.to_s] = method
|
271
|
+
@api_methods_full << [method.to_s, method.public_name.to_s]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def to_s
|
276
|
+
self.name.camelize
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ActionWebService
|
2
|
+
# To send simple types across the wire, derive from ActionWebService::Simple,
|
3
|
+
# and use +base+ to declare the base type for it restriction and,
|
4
|
+
# use +restriction+ to declare valid W3C restriction element for SimpleType.
|
5
|
+
#
|
6
|
+
# ActionWebService::Simple should be used when you want to declare valid custom simple type
|
7
|
+
# to be used inside expects, returns and structured types
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# There plenty documentation available at W3C Archives
|
11
|
+
# http://www.w3.org/TR/xmlschema-2/
|
12
|
+
#
|
13
|
+
# === Example
|
14
|
+
#
|
15
|
+
# class Gender < ActionWebService::Simple
|
16
|
+
# base :string
|
17
|
+
# restriction :enumeration, "male|female"
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
#
|
21
|
+
class Simple
|
22
|
+
|
23
|
+
def initialize(value)
|
24
|
+
@value = value
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def base(type)
|
29
|
+
type = type.to_s.camelize(:lower)
|
30
|
+
write_inheritable_attribute("xml_base", type)
|
31
|
+
class_eval <<-END
|
32
|
+
def xml_base; "#{type}"; end
|
33
|
+
END
|
34
|
+
end
|
35
|
+
|
36
|
+
def restriction_base
|
37
|
+
read_inheritable_attribute("xml_base") || ""
|
38
|
+
end
|
39
|
+
|
40
|
+
def restriction(type, value)
|
41
|
+
type = type.to_s.camelize(:lower)
|
42
|
+
write_inheritable_hash("simple_restrictions", type => value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def restrictions
|
46
|
+
read_inheritable_attribute("simple_restrictions") || {}
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StringToDatetimeForSoap
|
2
|
+
def to_datetime
|
3
|
+
begin
|
4
|
+
if /^([+\-]?\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ self.strip
|
5
|
+
return Time.xmlschema(self).localtime
|
6
|
+
end
|
7
|
+
super
|
8
|
+
rescue
|
9
|
+
# If a invalid date is supplied, it will automatically turn it into the first Julian Day
|
10
|
+
# Jan/01 - 4712 BC - This is a simple solution, until a better one appear :)
|
11
|
+
return Date.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
String.send :include, StringToDatetimeForSoap
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module ActionWebService
|
2
|
+
# To send structured types across the wire, derive from ActionWebService::Struct,
|
3
|
+
# and use +member+ to declare structure members.
|
4
|
+
#
|
5
|
+
# ActionWebService::Struct should be used in method signatures when you want to accept or return
|
6
|
+
# structured types that have no Active Record model class representations, or you don't
|
7
|
+
# want to expose your entire Active Record model to remote callers.
|
8
|
+
#
|
9
|
+
# === Example
|
10
|
+
#
|
11
|
+
# class Person < ActionWebService::Struct
|
12
|
+
# member :id, :int
|
13
|
+
# member :firstnames, [:string]
|
14
|
+
# member :lastname, :string
|
15
|
+
# member :email, :string, :nillable => true
|
16
|
+
# end
|
17
|
+
# person = Person.new(:id => 5, :firstname => 'john', :lastname => 'doe')
|
18
|
+
#
|
19
|
+
# Active Record model classes are already implicitly supported in method
|
20
|
+
# signatures.
|
21
|
+
class Struct
|
22
|
+
# If a Hash is given as argument to an ActionWebService::Struct constructor,
|
23
|
+
# it can contain initial values for the structure member.
|
24
|
+
# Values passed within the Hash that do not reflect member within the Struct will raise
|
25
|
+
# a NoMethodError unless the optional check_hash boolean is true.
|
26
|
+
def initialize(values={}, check_hash = false)
|
27
|
+
if values.is_a?(Hash)
|
28
|
+
values.map { |k,v| __send__("#{k}=", v) unless (check_hash && !self.respond_to?("#{k}=") ) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# The member with the given name
|
33
|
+
def [](name)
|
34
|
+
send(name.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Iterates through each member
|
38
|
+
def each_pair(&block)
|
39
|
+
self.class.members.each do |name, type|
|
40
|
+
yield name, self.__send__(name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class << self
|
45
|
+
# Creates a structure member with the specified +name+ and +type+. Additional wsdl
|
46
|
+
# schema properties may be specified in the optional hash +options+. Generates
|
47
|
+
# accessor methods for reading and writing the member value.
|
48
|
+
def member(name, type, options={})
|
49
|
+
name = name.to_sym
|
50
|
+
type = ActionWebService::SignatureTypes.canonical_signature_entry({ name => type }, 0)
|
51
|
+
write_inheritable_hash("struct_members", name => [type, options])
|
52
|
+
class_eval <<-END
|
53
|
+
def #{name}; @#{name}; end
|
54
|
+
def #{name}=(value); @#{name} = value; end
|
55
|
+
END
|
56
|
+
end
|
57
|
+
|
58
|
+
def members # :nodoc:
|
59
|
+
read_inheritable_attribute("struct_members") || {}
|
60
|
+
end
|
61
|
+
|
62
|
+
def member_type(name) # :nodoc:
|
63
|
+
type, options = members[name.to_sym]
|
64
|
+
type
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Class # :nodoc:
|
2
|
+
def class_inheritable_option(sym, default_value=nil)
|
3
|
+
write_inheritable_attribute sym, default_value
|
4
|
+
class_eval <<-EOS
|
5
|
+
def self.#{sym}(value=nil)
|
6
|
+
if !value.nil?
|
7
|
+
write_inheritable_attribute(:#{sym}, value)
|
8
|
+
else
|
9
|
+
read_inheritable_attribute(:#{sym})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.#{sym}=(value)
|
14
|
+
write_inheritable_attribute(:#{sym}, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def #{sym}
|
18
|
+
self.class.#{sym}
|
19
|
+
end
|
20
|
+
|
21
|
+
def #{sym}=(value)
|
22
|
+
self.class.#{sym} = value
|
23
|
+
end
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
end
|