aliyun-ess 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +5 -0
- data/INSTALL +13 -0
- data/bin/ess +6 -0
- data/bin/setup.rb +11 -0
- data/lib/aliyun/ess.rb +33 -0
- data/lib/aliyun/ess/authentication.rb +121 -0
- data/lib/aliyun/ess/base.rb +128 -0
- data/lib/aliyun/ess/collection.rb +24 -0
- data/lib/aliyun/ess/connection.rb +226 -0
- data/lib/aliyun/ess/error.rb +84 -0
- data/lib/aliyun/ess/exceptions.rb +82 -0
- data/lib/aliyun/ess/extensions.rb +174 -0
- data/lib/aliyun/ess/parsing.rb +67 -0
- data/lib/aliyun/ess/response.rb +161 -0
- data/lib/aliyun/ess/scaling_group.rb +50 -0
- data/lib/aliyun/ess/scaling_rule.rb +45 -0
- data/lib/aliyun/ess/service.rb +31 -0
- data/lib/aliyun/ess/version.rb +14 -0
- metadata +104 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Aliyun
|
3
|
+
module ESS
|
4
|
+
# Anything you do that makes a request to OSS could result in an error. If it does, the Aliyun::OSS library will raise an exception
|
5
|
+
# specific to the error. All exception that are raised as a result of a request returning an error response inherit from the
|
6
|
+
# ResponseError exception. So should you choose to rescue any such exception, you can simple rescue ResponseError.
|
7
|
+
#
|
8
|
+
# Say you go to delete a bucket, but the bucket turns out to not be empty. This results in a BucketNotEmpty error (one of the many
|
9
|
+
# errors listed at http://docs.aliyunwebservices.com/AliyunOSS/2006-03-01/ErrorCodeList.html):
|
10
|
+
#
|
11
|
+
# begin
|
12
|
+
# Bucket.delete('jukebox')
|
13
|
+
# rescue ResponseError => error
|
14
|
+
# # ...
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Once you've captured the exception, you can extract the error message from OSS, as well as the full error response, which includes
|
18
|
+
# things like the HTTP response code:
|
19
|
+
#
|
20
|
+
# error
|
21
|
+
# # => #<Aliyun::OSS::BucketNotEmpty The bucket you tried to delete is not empty>
|
22
|
+
# error.message
|
23
|
+
# # => "The bucket you tried to delete is not empty"
|
24
|
+
# error.response.code
|
25
|
+
# # => 409
|
26
|
+
#
|
27
|
+
# You could use this information to redisplay the error in a way you see fit, or just to log the error and continue on.
|
28
|
+
class Error
|
29
|
+
#:stopdoc:
|
30
|
+
attr_accessor :response
|
31
|
+
attr_reader :error, :exception, :container
|
32
|
+
|
33
|
+
def initialize(error, response = nil)
|
34
|
+
@error = error
|
35
|
+
@response = response
|
36
|
+
@container = Aliyun::ESS
|
37
|
+
find_or_create_exception!
|
38
|
+
end
|
39
|
+
|
40
|
+
def raise
|
41
|
+
Kernel.raise exception.new(message, response)
|
42
|
+
end
|
43
|
+
|
44
|
+
def code
|
45
|
+
@code ||= error['code'].sub('.', '::')
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def find_or_create_module!(modul)
|
51
|
+
container.const_defined?(modul) ? container.const_get(modul) : container.const_set(modul, Module.new)
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_or_create_exception!
|
55
|
+
@exception = container.const_defined?(code) ? find_exception : create_exception
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_exception
|
59
|
+
exception_class = container.const_get(code)
|
60
|
+
Kernel.raise ExceptionClassClash.new(exception_class) unless exception_class.ancestors.include?(ResponseError)
|
61
|
+
exception_class
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_exception
|
65
|
+
modul_or_clazz, clazz = code.split('::')
|
66
|
+
if clazz
|
67
|
+
find_or_create_module!(modul_or_clazz).const_set(clazz, Class.new(ResponseError))
|
68
|
+
else
|
69
|
+
container.const_set(modul_or_clazz, Class.new(ResponseError))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing(method, *args, &block)
|
74
|
+
# We actually want nil if the attribute is nil. So we use has_key? rather than [] + ||.
|
75
|
+
if error.has_key?(method.to_s)
|
76
|
+
error[method.to_s]
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
#:startdoc:
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Aliyun
|
3
|
+
module ESS
|
4
|
+
|
5
|
+
# Abstract super class of all Aliyun::OSS exceptions
|
6
|
+
class Exception < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
# All responses with a code between 300 and 599 that contain an <Error></Error> body are wrapped in an
|
10
|
+
# ErrorResponse which contains an Error object. This Error class generates a custom exception with the name
|
11
|
+
# of the xml Error and its message. All such runtime generated exception classes descend from ResponseError
|
12
|
+
# and contain the ErrorResponse object so that all code that makes a request can rescue ResponseError and get
|
13
|
+
# access to the ErrorResponse.
|
14
|
+
class ResponseError < Exception
|
15
|
+
attr_reader :response
|
16
|
+
def initialize(message, response)
|
17
|
+
@response = response
|
18
|
+
super(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#:stopdoc:
|
23
|
+
|
24
|
+
# Most ResponseError's are created just time on a need to have basis, but we explicitly define the
|
25
|
+
# InternalError exception because we want to explicitly rescue InternalError in some cases.
|
26
|
+
class InternalError < ResponseError
|
27
|
+
end
|
28
|
+
|
29
|
+
class NoSuchKey < ResponseError
|
30
|
+
end
|
31
|
+
|
32
|
+
class RequestTimeout < ResponseError
|
33
|
+
end
|
34
|
+
|
35
|
+
class InvalidParameter < ResponseError
|
36
|
+
end
|
37
|
+
|
38
|
+
# Abstract super class for all invalid options.
|
39
|
+
class InvalidOption < Exception
|
40
|
+
end
|
41
|
+
|
42
|
+
module IncorrectCapacity
|
43
|
+
end
|
44
|
+
|
45
|
+
# Raised if an invalid value is passed to the <tt>:access</tt> option when creating a Bucket or an OSSObject.
|
46
|
+
class InvalidAccessControlLevel < InvalidOption
|
47
|
+
def initialize(valid_levels, access_level)
|
48
|
+
super("Valid access control levels are #{valid_levels.inspect}. You specified `#{access_level}'.")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Raised if either the access key id or secret access key arguments are missing when establishing a connection.
|
53
|
+
class MissingAccessKey < InvalidOption
|
54
|
+
def initialize(missing_keys)
|
55
|
+
key_list = missing_keys.map {|key| key.to_s}.join(' and the ')
|
56
|
+
super("You did not provide both required access keys. Please provide the #{key_list}.")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Raised if a request is attempted before any connections have been established.
|
61
|
+
class NoConnectionEstablished < Exception
|
62
|
+
end
|
63
|
+
|
64
|
+
# Raised if an unrecognized option is passed when establishing a connection.
|
65
|
+
class InvalidConnectionOption < InvalidOption
|
66
|
+
def initialize(invalid_options)
|
67
|
+
message = "The following connection options are invalid: #{invalid_options.join(', ')}. " +
|
68
|
+
"The valid connection options are: #{Connection::Options::VALID_OPTIONS.join(', ')}."
|
69
|
+
super(message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ExceptionClassClash < Exception #:nodoc:
|
74
|
+
def initialize(klass)
|
75
|
+
message = "The exception class you tried to create (`#{klass}') exists and is not an exception"
|
76
|
+
super(message)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#:startdoc:
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
#:stopdoc:
|
3
|
+
|
4
|
+
class Hash
|
5
|
+
def slice(*keys)
|
6
|
+
keys.map! { |key| key.to_s }
|
7
|
+
keys.inject(Hash.new) { |hash, k| hash[k] = self[k] if has_key?(k); hash }
|
8
|
+
end unless public_method_defined? :slice
|
9
|
+
end
|
10
|
+
|
11
|
+
class String
|
12
|
+
# ActiveSupport adds an underscore method to String so let's just use that one if
|
13
|
+
# we find that the method is already defined
|
14
|
+
def underscore
|
15
|
+
gsub(/::/, '/').
|
16
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
17
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
18
|
+
tr("-", "_").downcase
|
19
|
+
end unless public_method_defined? :underscore
|
20
|
+
|
21
|
+
def camelize
|
22
|
+
string = to_s.sub(/^[a-z\d]*/) { $&.capitalize }
|
23
|
+
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
24
|
+
string.gsub!(/\//, '::')
|
25
|
+
string
|
26
|
+
end unless public_method_defined? :camelize
|
27
|
+
|
28
|
+
if RUBY_VERSION >= '1.9'
|
29
|
+
def valid_utf8?
|
30
|
+
dup.force_encoding('UTF-8').valid_encoding?
|
31
|
+
end
|
32
|
+
else
|
33
|
+
def valid_utf8?
|
34
|
+
scan(Regexp.new('[^\x00-\xa0]', nil, 'u')) { |s| s.unpack('U') }
|
35
|
+
true
|
36
|
+
rescue ArgumentError
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# All paths in in OSS have to be valid unicode so this takes care of
|
42
|
+
# cleaning up any strings that aren't valid utf-8 according to String#valid_utf8?
|
43
|
+
if RUBY_VERSION >= '1.9'
|
44
|
+
def remove_extended!
|
45
|
+
sanitized_string = ''
|
46
|
+
each_byte do |byte|
|
47
|
+
character = byte.chr
|
48
|
+
sanitized_string << character if character.ascii_only?
|
49
|
+
end
|
50
|
+
sanitized_string
|
51
|
+
end
|
52
|
+
else
|
53
|
+
def remove_extended!
|
54
|
+
#gsub!(/[\x80-\xFF]/) { "%02X" % $&[0] }
|
55
|
+
gsub!(Regext.new('/[\x80-\xFF]')) { "%02X" % $&[0] }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_extended
|
60
|
+
dup.remove_extended!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class CoercibleString < String
|
65
|
+
class << self
|
66
|
+
def coerce(string)
|
67
|
+
new(string).coerce
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def coerce
|
72
|
+
case self
|
73
|
+
when 'true'; true
|
74
|
+
when 'false'; false
|
75
|
+
# Don't coerce numbers that start with zero
|
76
|
+
when /^[1-9]+\d*$/; Integer(self)
|
77
|
+
when datetime_format; Time.parse(self)
|
78
|
+
else
|
79
|
+
self
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
# Lame hack since Date._parse is so accepting. OSS dates are of the form: '2006-10-29T23:14:47.000Z'
|
85
|
+
# so unless the string looks like that, don't even try, otherwise it might convert an object's
|
86
|
+
# key from something like '03 1-2-3-Apple-Tree.mp3' to Sat Feb 03 00:00:00 CST 2001.
|
87
|
+
def datetime_format
|
88
|
+
/^\d{4}-\d{2}-\d{2}\w\d{2}:\d{2}:\d{2}/
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Class # :nodoc:
|
93
|
+
def cattr_reader(*syms)
|
94
|
+
syms.flatten.each do |sym|
|
95
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
96
|
+
unless defined? @@#{sym}
|
97
|
+
@@#{sym} = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.#{sym}
|
101
|
+
@@#{sym}
|
102
|
+
end
|
103
|
+
|
104
|
+
def #{sym}
|
105
|
+
@@#{sym}
|
106
|
+
end
|
107
|
+
EOS
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def cattr_writer(*syms)
|
112
|
+
syms.flatten.each do |sym|
|
113
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
114
|
+
unless defined? @@#{sym}
|
115
|
+
@@#{sym} = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.#{sym}=(obj)
|
119
|
+
@@#{sym} = obj
|
120
|
+
end
|
121
|
+
|
122
|
+
def #{sym}=(obj)
|
123
|
+
@@#{sym} = obj
|
124
|
+
end
|
125
|
+
EOS
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def cattr_accessor(*syms)
|
130
|
+
cattr_reader(*syms)
|
131
|
+
cattr_writer(*syms)
|
132
|
+
end
|
133
|
+
end if Class.instance_methods(false).grep(/^cattr_(?:reader|writer|accessor)$/).empty?
|
134
|
+
|
135
|
+
module SelectiveAttributeProxy
|
136
|
+
def self.included(klass)
|
137
|
+
klass.extend(ClassMethods)
|
138
|
+
klass.class_eval(<<-EVAL, __FILE__, __LINE__)
|
139
|
+
cattr_accessor :attribute_proxy
|
140
|
+
cattr_accessor :attribute_proxy_options
|
141
|
+
|
142
|
+
# Default name for attribute storage
|
143
|
+
self.attribute_proxy = :attributes
|
144
|
+
self.attribute_proxy_options = {:exclusively => true}
|
145
|
+
|
146
|
+
private
|
147
|
+
# By default proxy all attributes
|
148
|
+
def proxiable_attribute?(name)
|
149
|
+
return true unless self.class.attribute_proxy_options[:exclusively]
|
150
|
+
send(self.class.attribute_proxy).has_key?(name)
|
151
|
+
end
|
152
|
+
|
153
|
+
def method_missing(method, *args, &block)
|
154
|
+
# Autovivify attribute storage
|
155
|
+
if method == self.class.attribute_proxy
|
156
|
+
ivar = "@\#{method}"
|
157
|
+
instance_variable_set(ivar, {}) unless instance_variable_get(ivar).is_a?(Hash)
|
158
|
+
instance_variable_get(ivar)
|
159
|
+
# Delegate to attribute storage
|
160
|
+
elsif method.to_s =~ /^(\\w+)(=?)$/ && proxiable_attribute?($1)
|
161
|
+
attributes_hash_name = self.class.attribute_proxy
|
162
|
+
$2.empty? ? send(attributes_hash_name)[$1] : send(attributes_hash_name)[$1] = args.first
|
163
|
+
else
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
EVAL
|
168
|
+
end
|
169
|
+
|
170
|
+
module ClassMethods
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
#:startdoc:
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
#:stopdoc:
|
3
|
+
module Aliyun
|
4
|
+
module ESS
|
5
|
+
module Parsing
|
6
|
+
|
7
|
+
module Typecasting
|
8
|
+
def typecast(object)
|
9
|
+
case object
|
10
|
+
when Hash
|
11
|
+
typecast_hash(object)
|
12
|
+
when Array
|
13
|
+
object.map {|element| typecast(element)}
|
14
|
+
when String
|
15
|
+
CoercibleString.coerce(object)
|
16
|
+
else
|
17
|
+
object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def typecast_hash(hash)
|
22
|
+
keys = hash.keys.map {|key| key.underscore}
|
23
|
+
values = hash.values.map {|value| typecast(value)}
|
24
|
+
keys.inject({}) do |new_hash, key|
|
25
|
+
new_hash[key] = values.slice!(0)
|
26
|
+
new_hash
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class JsonParser < Hash
|
32
|
+
include Typecasting
|
33
|
+
|
34
|
+
attr_reader :body, :code
|
35
|
+
|
36
|
+
def initialize(body)
|
37
|
+
@body = body
|
38
|
+
unless body.strip.empty?
|
39
|
+
parse
|
40
|
+
typecast_json_parsed
|
41
|
+
set_code
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def parse
|
48
|
+
@json_parsed = JSON.parse(body)
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_code
|
52
|
+
@code = self['code']
|
53
|
+
end
|
54
|
+
|
55
|
+
def typecast_json_parsed
|
56
|
+
typecast_json = {}
|
57
|
+
@json_parsed.dup.each do |key, value| # Some typecasting is destructive so we dup
|
58
|
+
typecast_json[key.underscore] = typecast(value)
|
59
|
+
end
|
60
|
+
# An empty body will try to update with a string so only update if the result is a hash
|
61
|
+
update(typecast_json) unless typecast_json.keys.size==0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
#:startdoc:
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
#:stopdoc:
|
3
|
+
module Aliyun
|
4
|
+
module ESS
|
5
|
+
class Base
|
6
|
+
class Response < String
|
7
|
+
attr_reader :response, :body, :parsed
|
8
|
+
def initialize(response)
|
9
|
+
@response = response
|
10
|
+
@body = response.body.to_s
|
11
|
+
super(body)
|
12
|
+
end
|
13
|
+
|
14
|
+
def headers
|
15
|
+
@headers ||= begin
|
16
|
+
headers = {}
|
17
|
+
response.each do |header, value|
|
18
|
+
headers[header] = value
|
19
|
+
end
|
20
|
+
headers
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](header)
|
25
|
+
headers[header]
|
26
|
+
end
|
27
|
+
|
28
|
+
def each(&block)
|
29
|
+
headers.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def code
|
33
|
+
response.code.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
{:success => 200..299, :redirect => 300..399,
|
37
|
+
:client_error => 400..499, :server_error => 500..599}.each do |result, code_range|
|
38
|
+
class_eval(<<-EVAL, __FILE__, __LINE__)
|
39
|
+
def #{result}?
|
40
|
+
return false unless response
|
41
|
+
(#{code_range}).include? code
|
42
|
+
end
|
43
|
+
EVAL
|
44
|
+
end
|
45
|
+
|
46
|
+
def error?
|
47
|
+
!success? && !parsed.code.nil?
|
48
|
+
end
|
49
|
+
|
50
|
+
def error
|
51
|
+
@error ||= Error.new(parsed, self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def parsed
|
55
|
+
# XmlSimple is picky about what kind of object it parses, so we pass in body rather than self
|
56
|
+
@parsed ||= Parsing::JsonParser.new(body)
|
57
|
+
end
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
"#<%s:0x%s %s %s>" % [self.class, object_id, response.code, response.message]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class ScalingGroup
|
66
|
+
class Response < Base::Response
|
67
|
+
def items
|
68
|
+
(parsed['scaling_groups'] && parsed['scaling_groups']['scaling_group']) || []
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ScalingRule
|
74
|
+
class Response < Base::Response
|
75
|
+
def items
|
76
|
+
(parsed['scaling_rules'] && parsed['scaling_rules']['scaling_rule']) || []
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Requests whose response code is between 300 and 599 and contain an <Error></Error> in their body
|
83
|
+
# are wrapped in an Error::Response. This Error::Response contains an Error object which raises an exception
|
84
|
+
# that corresponds to the error in the response body. The exception object contains the ErrorResponse, so
|
85
|
+
# in all cases where a request happens, you can rescue ResponseError and have access to the ErrorResponse and
|
86
|
+
# its Error object which contains information about the ResponseError.
|
87
|
+
#
|
88
|
+
# begin
|
89
|
+
# Bucket.create(..)
|
90
|
+
# rescue ResponseError => exception
|
91
|
+
# exception.response
|
92
|
+
# # => <Error::Response>
|
93
|
+
# exception.response.error
|
94
|
+
# # => <Error>
|
95
|
+
# end
|
96
|
+
class Error
|
97
|
+
class Response < Base::Response
|
98
|
+
def error?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def inspect
|
103
|
+
"#<%s:0x%s %s %s: '%s'>" % [self.class.name, object_id, response.code, error.code, error.message]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Guess response class name from current class name. If the guessed response class doesn't exist
|
109
|
+
# do the same thing to the current class's parent class, up the inheritance heirarchy until either
|
110
|
+
# a response class is found or until we get to the top of the heirarchy in which case we just use
|
111
|
+
# the the Base response class.
|
112
|
+
#
|
113
|
+
# Important: This implemantation assumes that the Base class has a corresponding Base::Response.
|
114
|
+
class FindResponseClass #:nodoc:
|
115
|
+
class << self
|
116
|
+
def for(start)
|
117
|
+
new(start).find
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(start)
|
122
|
+
@container = Aliyun::ESS
|
123
|
+
@current_class = start
|
124
|
+
end
|
125
|
+
|
126
|
+
def find
|
127
|
+
self.current_class = current_class.superclass until response_class_found?
|
128
|
+
target.const_get(class_to_find)
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
attr_reader :container
|
133
|
+
attr_accessor :current_class
|
134
|
+
|
135
|
+
def target
|
136
|
+
container.const_get(current_name)
|
137
|
+
end
|
138
|
+
|
139
|
+
def target?
|
140
|
+
container.const_defined?(current_name)
|
141
|
+
end
|
142
|
+
|
143
|
+
def response_class_found?
|
144
|
+
target? && target.const_defined?(class_to_find)
|
145
|
+
end
|
146
|
+
|
147
|
+
def class_to_find
|
148
|
+
:Response
|
149
|
+
end
|
150
|
+
|
151
|
+
def current_name
|
152
|
+
truncate(current_class)
|
153
|
+
end
|
154
|
+
|
155
|
+
def truncate(klass)
|
156
|
+
klass.name[/[^:]+$/]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
#:startdoc:
|