peleteiro-activecouch 0.2.1
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 +28 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/active_couch.rb +12 -0
- data/lib/active_couch/base.rb +608 -0
- data/lib/active_couch/callbacks.rb +89 -0
- data/lib/active_couch/connection.rb +164 -0
- data/lib/active_couch/errors.rb +13 -0
- data/lib/active_couch/support.rb +3 -0
- data/lib/active_couch/support/exporter.rb +97 -0
- data/lib/active_couch/support/extensions.rb +86 -0
- data/lib/active_couch/support/inflections.rb +52 -0
- data/lib/active_couch/support/inflector.rb +279 -0
- data/lib/active_couch/views.rb +3 -0
- data/lib/active_couch/views/errors.rb +4 -0
- data/lib/active_couch/views/raw_view.rb +40 -0
- data/lib/active_couch/views/view.rb +85 -0
- data/lib/activecouch.rb +1 -0
- data/spec/base/after_delete_spec.rb +110 -0
- data/spec/base/after_save_spec.rb +102 -0
- data/spec/base/before_delete_spec.rb +109 -0
- data/spec/base/before_save_spec.rb +101 -0
- data/spec/base/count_all_spec.rb +29 -0
- data/spec/base/count_spec.rb +77 -0
- data/spec/base/create_spec.rb +28 -0
- data/spec/base/database_spec.rb +70 -0
- data/spec/base/delete_spec.rb +97 -0
- data/spec/base/find_from_url_spec.rb +55 -0
- data/spec/base/find_spec.rb +383 -0
- data/spec/base/from_json_spec.rb +54 -0
- data/spec/base/has_many_spec.rb +89 -0
- data/spec/base/has_spec.rb +88 -0
- data/spec/base/id_spec.rb +25 -0
- data/spec/base/initialize_spec.rb +91 -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 +18 -0
- data/spec/base/nested_class_spec.rb +19 -0
- data/spec/base/rev_spec.rb +20 -0
- data/spec/base/save_spec.rb +130 -0
- data/spec/base/site_spec.rb +62 -0
- data/spec/base/to_json_spec.rb +73 -0
- data/spec/connection/initialize_spec.rb +28 -0
- data/spec/exporter/all_databases_spec.rb +24 -0
- data/spec/exporter/create_database_spec.rb +47 -0
- data/spec/exporter/delete_database_spec.rb +45 -0
- data/spec/exporter/delete_spec.rb +36 -0
- data/spec/exporter/export_spec.rb +62 -0
- data/spec/exporter/export_with_raw_views_spec.rb +66 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/views/define_spec.rb +34 -0
- data/spec/views/include_attributes_spec.rb +30 -0
- data/spec/views/raw_view_spec.rb +49 -0
- data/spec/views/to_json_spec.rb +58 -0
- data/spec/views/with_filter_spec.rb +13 -0
- data/spec/views/with_key_spec.rb +19 -0
- metadata +117 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module ActiveCouch
|
|
2
|
+
module Callbacks
|
|
3
|
+
CALLBACKS = %w(before_save after_save before_delete after_delete)
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
# Alias methods which will have callbacks, (for now only save and delete).
|
|
7
|
+
# This creates 2 pairs of methods: save_with_callbacks, save_without_callbacks,
|
|
8
|
+
# delete_with_callbacks, delete_without_callbacks
|
|
9
|
+
#
|
|
10
|
+
# save_without_callbacks and delete_without_callbacks
|
|
11
|
+
# have the same behaviour as the save and delete methods, respectively
|
|
12
|
+
[:save, :delete].each do |method|
|
|
13
|
+
base.send :alias_method_chain, method, :callbacks
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
CALLBACKS.each do |method|
|
|
17
|
+
base.class_eval <<-"end_eval"
|
|
18
|
+
def self.#{method}(*callbacks, &block)
|
|
19
|
+
callbacks << block if block_given?
|
|
20
|
+
# Assumes that the default value for the callbacks hash in the
|
|
21
|
+
# including class is an empty array
|
|
22
|
+
self.callbacks[#{method.to_sym.inspect}] = self.callbacks[#{method.to_sym.inspect}] + callbacks
|
|
23
|
+
end
|
|
24
|
+
end_eval
|
|
25
|
+
end
|
|
26
|
+
end # end method self.included
|
|
27
|
+
|
|
28
|
+
def before_save() end
|
|
29
|
+
|
|
30
|
+
def after_save() end
|
|
31
|
+
|
|
32
|
+
def before_delete() end
|
|
33
|
+
|
|
34
|
+
def after_delete() end
|
|
35
|
+
|
|
36
|
+
def save_with_callbacks(opts = {})
|
|
37
|
+
return false if callback(:before_save) == false
|
|
38
|
+
result = save_without_callbacks(opts)
|
|
39
|
+
callback(:after_save)
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
private :save_with_callbacks
|
|
43
|
+
|
|
44
|
+
def delete_with_callbacks(opts = {})
|
|
45
|
+
return false if callback(:before_delete) == false
|
|
46
|
+
result = delete_without_callbacks(opts)
|
|
47
|
+
callback(:after_delete)
|
|
48
|
+
result
|
|
49
|
+
end
|
|
50
|
+
private :delete_with_callbacks
|
|
51
|
+
|
|
52
|
+
def find_with_callbacks
|
|
53
|
+
return false if callback(:before_find) == false
|
|
54
|
+
result = find_without_callbacks
|
|
55
|
+
callback(:after_find)
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
private :find_with_callbacks
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
def callback(method)
|
|
62
|
+
callbacks_for(method).each do |callback|
|
|
63
|
+
result = case callback
|
|
64
|
+
when Symbol
|
|
65
|
+
self.send(callback)
|
|
66
|
+
when String
|
|
67
|
+
eval(callback, binding)
|
|
68
|
+
when Proc, Method
|
|
69
|
+
callback.call(self)
|
|
70
|
+
else
|
|
71
|
+
if callback.respond_to?(method)
|
|
72
|
+
callback.send(method, self)
|
|
73
|
+
else
|
|
74
|
+
raise ActiveCouchError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
return false if result == false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
result = send(method) if respond_to?(method)
|
|
81
|
+
|
|
82
|
+
return result
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def callbacks_for(method)
|
|
86
|
+
self.class.callbacks[method.to_sym]
|
|
87
|
+
end
|
|
88
|
+
end # end module Callbacks
|
|
89
|
+
end # end module ActiveCouch
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Connection class borrowed from ActiveResource
|
|
2
|
+
require 'net/https'
|
|
3
|
+
require 'date'
|
|
4
|
+
require 'time'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require 'benchmark'
|
|
7
|
+
|
|
8
|
+
module ActiveCouch
|
|
9
|
+
class ConnectionError < StandardError # :nodoc:
|
|
10
|
+
attr_reader :response
|
|
11
|
+
|
|
12
|
+
def initialize(response, message = nil)
|
|
13
|
+
@response = response
|
|
14
|
+
@message = message
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# 3xx Redirection
|
|
23
|
+
class Redirection < ConnectionError # :nodoc:
|
|
24
|
+
def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# 4xx Client Error
|
|
28
|
+
class ClientError < ConnectionError; end # :nodoc:
|
|
29
|
+
|
|
30
|
+
# 404 Not Found
|
|
31
|
+
class ResourceNotFound < ClientError; end # :nodoc:
|
|
32
|
+
|
|
33
|
+
# 409 Conflict
|
|
34
|
+
class ResourceConflict < ClientError; end # :nodoc:
|
|
35
|
+
|
|
36
|
+
# 412 Precondition Failed - this is returned when there is an update conflict (usually means revisions don't match).
|
|
37
|
+
class UpdateConflict < ClientError # :nodoc:
|
|
38
|
+
def to_s
|
|
39
|
+
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}. Body: #{response.body}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 5xx Server Error
|
|
44
|
+
class ServerError < ConnectionError; end # :nodoc:
|
|
45
|
+
|
|
46
|
+
# 405 Method Not Allowed
|
|
47
|
+
class MethodNotAllowed < ClientError # :nodoc:
|
|
48
|
+
def allowed_methods
|
|
49
|
+
@response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Class to handle connections to remote web services.
|
|
54
|
+
# This class is used by ActiveCouch::Base to interface with REST
|
|
55
|
+
# services.
|
|
56
|
+
class Connection
|
|
57
|
+
attr_reader :site
|
|
58
|
+
|
|
59
|
+
class << self
|
|
60
|
+
def requests
|
|
61
|
+
@@requests ||= []
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The +site+ parameter is required and will set the +site+
|
|
66
|
+
# attribute to the URI for the remote resource service.
|
|
67
|
+
def initialize(site)
|
|
68
|
+
raise ArgumentError, 'Missing site URI' unless site
|
|
69
|
+
init_site_with_path(site)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Set URI for remote service.
|
|
73
|
+
def site=(site)
|
|
74
|
+
@site = site.is_a?(URI) ? site : URI.parse(site)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Execute a GET request.
|
|
78
|
+
# Used to get (find) resources.
|
|
79
|
+
def get(path, headers = {})
|
|
80
|
+
request(:get, path, build_request_headers(headers)).body
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Execute a DELETE request (see HTTP protocol documentation if unfamiliar).
|
|
84
|
+
# Used to delete resources.
|
|
85
|
+
def delete(path, headers = {})
|
|
86
|
+
request(:delete, path, build_request_headers(headers))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Execute a PUT request (see HTTP protocol documentation if unfamiliar).
|
|
90
|
+
# Used to update resources.
|
|
91
|
+
def put(path, body = '', headers = {})
|
|
92
|
+
request(:put, path, body.to_s, build_request_headers(headers))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Execute a POST request.
|
|
96
|
+
# Used to create new resources.
|
|
97
|
+
def post(path, body = '', headers = {})
|
|
98
|
+
request(:post, path, body.to_s, build_request_headers(headers))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
# Makes request to remote service.
|
|
104
|
+
def request(method, path, *arguments)
|
|
105
|
+
result = nil
|
|
106
|
+
time = Benchmark.realtime { result = http.send(method, path, *arguments) }
|
|
107
|
+
handle_response(result)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Handles response and error codes from remote service.
|
|
111
|
+
def handle_response(response)
|
|
112
|
+
case response.code.to_i
|
|
113
|
+
when 301,302
|
|
114
|
+
raise(Redirection.new(response))
|
|
115
|
+
when 200...400
|
|
116
|
+
response
|
|
117
|
+
when 404
|
|
118
|
+
raise(ResourceNotFound.new(response))
|
|
119
|
+
when 405
|
|
120
|
+
raise(MethodNotAllowed.new(response))
|
|
121
|
+
when 409
|
|
122
|
+
raise(ResourceConflict.new(response))
|
|
123
|
+
when 412
|
|
124
|
+
raise(UpdateConflict.new(response))
|
|
125
|
+
when 422
|
|
126
|
+
raise(ResourceInvalid.new(response))
|
|
127
|
+
when 401...500
|
|
128
|
+
raise(ClientError.new(response))
|
|
129
|
+
when 500...600
|
|
130
|
+
raise(ServerError.new(response))
|
|
131
|
+
else
|
|
132
|
+
raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Creates new Net::HTTP instance for communication with
|
|
137
|
+
# remote service and resources.
|
|
138
|
+
def http
|
|
139
|
+
http = Net::HTTP.new(@site.host, @site.port)
|
|
140
|
+
http.use_ssl = @site.is_a?(URI::HTTPS)
|
|
141
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
|
142
|
+
http
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def default_header
|
|
146
|
+
@default_header ||= { 'Content-Type' => 'application/json' }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Builds headers for request to remote service.
|
|
150
|
+
def build_request_headers(headers)
|
|
151
|
+
authorization_header.update(default_header).update(headers)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Sets authorization header; authentication information is pulled from credentials provided with site URI.
|
|
155
|
+
def authorization_header
|
|
156
|
+
(@site.user || @site.password ? { 'Authorization' => 'Basic ' + ["#{@site.user}:#{ @site.password}"].pack('m').delete("\r\n") } : {})
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def init_site_with_path(site)
|
|
160
|
+
site = "#{site}:5984" if site.is_a?(String) && (site =~ /http\:\/\/(.*?)\:(\d+)/).nil?
|
|
161
|
+
@site = URI.parse(site)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module ActiveCouch
|
|
2
|
+
# Base exception class for all ActiveCouch errors.
|
|
3
|
+
class ActiveCouchError < StandardError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
# Raised when there is a configuration error (duh).
|
|
7
|
+
class ConfigurationError < ActiveCouchError
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Raised when trying to get or set a non-existent attribute.
|
|
11
|
+
class AttributeMissingError < ActiveCouchError
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# TODO
|
|
2
|
+
# - might consider moving create_database and delete_database to an adapter type class to encapsulate CouchDB semantics
|
|
3
|
+
# and responses.
|
|
4
|
+
module ActiveCouch
|
|
5
|
+
class Exporter
|
|
6
|
+
class << self # Class methods
|
|
7
|
+
|
|
8
|
+
def all_databases(site)
|
|
9
|
+
conn = Connection.new(site)
|
|
10
|
+
JSON.parse(conn.get("/_all_dbs"))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def export(site, view, opts = {})
|
|
14
|
+
existing_view = {}
|
|
15
|
+
if view.name.nil? || (view.database.nil? && opts[:database].nil?)
|
|
16
|
+
raise ActiveCouch::ViewError, "Both the name and the database need to be defined in your view"
|
|
17
|
+
end
|
|
18
|
+
# If the database is not defined in the view, it can be supported
|
|
19
|
+
# as an option to the export method
|
|
20
|
+
database_name = opts[:database] || view.database
|
|
21
|
+
conn = Connection.new(site)
|
|
22
|
+
# The view function for a view with name 'by_name' and database 'activecouch_test' should be PUT to
|
|
23
|
+
# http://#{host}:#{port}/activecouch_test/_design/by_name.
|
|
24
|
+
if(view_json = exists?(site, "/#{database_name}/_design/#{view.name}"))
|
|
25
|
+
existing_view = JSON.parse(view_json)
|
|
26
|
+
end
|
|
27
|
+
response = conn.put("/#{database_name}/_design/#{view.name}", view.to_json(existing_view))
|
|
28
|
+
case response.code
|
|
29
|
+
when '201'
|
|
30
|
+
true # 201 = success
|
|
31
|
+
else
|
|
32
|
+
raise ActiveCouch::ViewError, "Error exporting view - got HTTP response #{response.code}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete(site, view, opts = {})
|
|
37
|
+
rev = nil
|
|
38
|
+
if view.name.nil? || (view.database.nil? && opts[:database].nil?)
|
|
39
|
+
raise ActiveCouch::ViewError, "Both the name and the database need to be defined in your view"
|
|
40
|
+
end
|
|
41
|
+
# If the database is not defined in the view, it can be supported
|
|
42
|
+
# as an option to the export method
|
|
43
|
+
database_name = opts[:database] || view.database
|
|
44
|
+
conn = Connection.new(site)
|
|
45
|
+
if(view_json = exists?(site, "/#{database_name}/_design/#{view.name}"))
|
|
46
|
+
rev = JSON.parse(view_json)['_rev']
|
|
47
|
+
end
|
|
48
|
+
# The view function for a view with name 'by_name' and database 'activecouch_test' should be PUT to
|
|
49
|
+
# http://#{host}:#{port}/activecouch_test/_design/by_name.
|
|
50
|
+
response = conn.delete("/#{database_name}/_design/#{view.name}?rev=#{rev}")
|
|
51
|
+
if response.code =~ /20[0,2]/
|
|
52
|
+
true # 20[0,2] = success
|
|
53
|
+
else
|
|
54
|
+
raise ActiveCouch::ViewError, "Error deleting view - got HTTP response #{response.code}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def exists?(site, name)
|
|
59
|
+
conn = Connection.new(site)
|
|
60
|
+
response = conn.get("#{name}")
|
|
61
|
+
response
|
|
62
|
+
rescue ActiveCouch::ResourceNotFound
|
|
63
|
+
false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def create_database(site, name)
|
|
67
|
+
conn = Connection.new(site)
|
|
68
|
+
response = conn.put("/#{name}", "{}")
|
|
69
|
+
|
|
70
|
+
case response.code
|
|
71
|
+
when '201' # 201 = success
|
|
72
|
+
true
|
|
73
|
+
when '409' # 409 = database already exists
|
|
74
|
+
raise ActiveCouch::ViewError, 'Database exists'
|
|
75
|
+
else
|
|
76
|
+
raise ActiveCouch::ViewError, "Error creating database - got HTTP response #{response.code}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def delete_database(site, name)
|
|
81
|
+
conn = Connection.new(site)
|
|
82
|
+
response = conn.delete("/#{name}")
|
|
83
|
+
|
|
84
|
+
case response.code
|
|
85
|
+
when '200'
|
|
86
|
+
when '201'
|
|
87
|
+
when '202'
|
|
88
|
+
true # 201 = success
|
|
89
|
+
when '404'
|
|
90
|
+
raise ActiveCouch::ViewError, "Database '#{name}' does not exist" # 404 = database doesn't exist
|
|
91
|
+
else
|
|
92
|
+
raise ActiveCouch::ViewError, "Error creating database - got HTTP response #{response.code}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module ActiveCouch
|
|
2
|
+
|
|
3
|
+
Symbol.class_eval do
|
|
4
|
+
def singularize; ActiveCouch::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; ActiveCouch::Inflector.singularize(self); end
|
|
12
|
+
def demodulize; ActiveCouch::Inflector.demodulize(self); end
|
|
13
|
+
def pluralize; ActiveCouch::Inflector.pluralize(self); end
|
|
14
|
+
def underscore; ActiveCouch::Inflector.underscore(self); end
|
|
15
|
+
def classify; ActiveCouch::Inflector.classify(self); end
|
|
16
|
+
def constantize; ActiveCouch::Inflector.constantize(self); end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Array.class_eval do
|
|
20
|
+
def extract_options!
|
|
21
|
+
last.is_a?(::Hash) ? pop : {}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Hash.class_eval do
|
|
26
|
+
# Flatten on the array removes everything into *one* single array,
|
|
27
|
+
# so {}.to_a.flatten sometimes won't work nicely because a value might be an array
|
|
28
|
+
# So..introducing flatten for Hash, so that arrays which are values (to keys)
|
|
29
|
+
# are retained
|
|
30
|
+
def flatten
|
|
31
|
+
(0...self.size).inject([]) {|k,v| k << self.keys[v]; k << self.values[v]}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Object.class_eval do
|
|
36
|
+
def get_class(name)
|
|
37
|
+
# From 'The Ruby Way Second Edition' by Hal Fulton
|
|
38
|
+
# This is to get nested class for e.g. A::B::C
|
|
39
|
+
name.split("::").inject(Object) {|x,y| x.const_get(y)}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The singleton class.
|
|
43
|
+
def metaclass; class << self; self; end; end
|
|
44
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
|
45
|
+
|
|
46
|
+
# Adds methods to a metaclass.
|
|
47
|
+
def meta_def name, &blk
|
|
48
|
+
meta_eval { define_method name, &blk }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Defines an instance method within a class.
|
|
52
|
+
def class_def name, &blk
|
|
53
|
+
class_eval { define_method name, &blk }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Module.module_eval do
|
|
58
|
+
# Return the module which contains this one; if this is a root module, such as
|
|
59
|
+
# +::MyModule+, then Object is returned.
|
|
60
|
+
def parent
|
|
61
|
+
parent_name = name.split('::')[0..-2] * '::'
|
|
62
|
+
parent_name.empty? ? Object : ActiveCouch::Inflector.constantize(parent_name)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def alias_method_chain(target, feature)
|
|
66
|
+
# Strip out punctuation on predicates or bang methods since
|
|
67
|
+
# e.g. target?_without_feature is not a valid method name.
|
|
68
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
|
69
|
+
yield(aliased_target, punctuation) if block_given?
|
|
70
|
+
|
|
71
|
+
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
|
|
72
|
+
|
|
73
|
+
alias_method without_method, target
|
|
74
|
+
alias_method target, with_method
|
|
75
|
+
|
|
76
|
+
case
|
|
77
|
+
when public_method_defined?(without_method)
|
|
78
|
+
public target
|
|
79
|
+
when protected_method_defined?(without_method)
|
|
80
|
+
protected target
|
|
81
|
+
when private_method_defined?(without_method)
|
|
82
|
+
private target
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|