cardiac 0.2.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/LICENSE +22 -0
- data/Rakefile +66 -0
- data/cardiac-0.2.0.pre2.gem +0 -0
- data/cardiac.gemspec +48 -0
- data/lib/cardiac/declarations.rb +70 -0
- data/lib/cardiac/errors.rb +65 -0
- data/lib/cardiac/log_subscriber.rb +55 -0
- data/lib/cardiac/model/attributes.rb +146 -0
- data/lib/cardiac/model/base.rb +161 -0
- data/lib/cardiac/model/callbacks.rb +47 -0
- data/lib/cardiac/model/declarations.rb +106 -0
- data/lib/cardiac/model/dirty.rb +117 -0
- data/lib/cardiac/model/locale/en.yml +7 -0
- data/lib/cardiac/model/operations.rb +49 -0
- data/lib/cardiac/model/persistence.rb +171 -0
- data/lib/cardiac/model/querying.rb +129 -0
- data/lib/cardiac/model/validations.rb +124 -0
- data/lib/cardiac/model.rb +17 -0
- data/lib/cardiac/operation_builder.rb +75 -0
- data/lib/cardiac/operation_handler.rb +215 -0
- data/lib/cardiac/railtie.rb +20 -0
- data/lib/cardiac/reflections.rb +85 -0
- data/lib/cardiac/representation.rb +124 -0
- data/lib/cardiac/resource/adapter.rb +178 -0
- data/lib/cardiac/resource/builder.rb +107 -0
- data/lib/cardiac/resource/codec_methods.rb +58 -0
- data/lib/cardiac/resource/config_methods.rb +39 -0
- data/lib/cardiac/resource/extension_methods.rb +115 -0
- data/lib/cardiac/resource/request_methods.rb +138 -0
- data/lib/cardiac/resource/subresource.rb +88 -0
- data/lib/cardiac/resource/uri_methods.rb +176 -0
- data/lib/cardiac/resource.rb +77 -0
- data/lib/cardiac/util.rb +120 -0
- data/lib/cardiac/version.rb +3 -0
- data/lib/cardiac.rb +61 -0
- data/spec/rails-3.2/Gemfile +9 -0
- data/spec/rails-3.2/Gemfile.lock +136 -0
- data/spec/rails-3.2/Rakefile +10 -0
- data/spec/rails-3.2/app_root/app/assets/javascripts/application.js +15 -0
- data/spec/rails-3.2/app_root/app/assets/stylesheets/application.css +13 -0
- data/spec/rails-3.2/app_root/app/controllers/application_controller.rb +3 -0
- data/spec/rails-3.2/app_root/app/helpers/application_helper.rb +2 -0
- data/spec/rails-3.2/app_root/app/views/layouts/application.html.erb +14 -0
- data/spec/rails-3.2/app_root/config/application.rb +29 -0
- data/spec/rails-3.2/app_root/config/boot.rb +13 -0
- data/spec/rails-3.2/app_root/config/database.yml +25 -0
- data/spec/rails-3.2/app_root/config/environment.rb +5 -0
- data/spec/rails-3.2/app_root/config/environments/development.rb +10 -0
- data/spec/rails-3.2/app_root/config/environments/production.rb +11 -0
- data/spec/rails-3.2/app_root/config/environments/test.rb +11 -0
- data/spec/rails-3.2/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails-3.2/app_root/config/initializers/inflections.rb +15 -0
- data/spec/rails-3.2/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails-3.2/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails-3.2/app_root/config/initializers/session_store.rb +8 -0
- data/spec/rails-3.2/app_root/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails-3.2/app_root/config/locales/en.yml +5 -0
- data/spec/rails-3.2/app_root/config/routes.rb +2 -0
- data/spec/rails-3.2/app_root/db/test.sqlite3 +0 -0
- data/spec/rails-3.2/app_root/log/test.log +2403 -0
- data/spec/rails-3.2/app_root/public/404.html +26 -0
- data/spec/rails-3.2/app_root/public/422.html +26 -0
- data/spec/rails-3.2/app_root/public/500.html +25 -0
- data/spec/rails-3.2/app_root/public/favicon.ico +0 -0
- data/spec/rails-3.2/app_root/script/rails +6 -0
- data/spec/rails-3.2/spec/spec_helper.rb +25 -0
- data/spec/rails-4.0/Gemfile +9 -0
- data/spec/rails-4.0/Gemfile.lock +132 -0
- data/spec/rails-4.0/Rakefile +10 -0
- data/spec/rails-4.0/app_root/app/assets/javascripts/application.js +15 -0
- data/spec/rails-4.0/app_root/app/assets/stylesheets/application.css +13 -0
- data/spec/rails-4.0/app_root/app/controllers/application_controller.rb +3 -0
- data/spec/rails-4.0/app_root/app/helpers/application_helper.rb +2 -0
- data/spec/rails-4.0/app_root/app/views/layouts/application.html.erb +14 -0
- data/spec/rails-4.0/app_root/config/application.rb +28 -0
- data/spec/rails-4.0/app_root/config/boot.rb +13 -0
- data/spec/rails-4.0/app_root/config/database.yml +25 -0
- data/spec/rails-4.0/app_root/config/environment.rb +5 -0
- data/spec/rails-4.0/app_root/config/environments/development.rb +9 -0
- data/spec/rails-4.0/app_root/config/environments/production.rb +11 -0
- data/spec/rails-4.0/app_root/config/environments/test.rb +10 -0
- data/spec/rails-4.0/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails-4.0/app_root/config/initializers/inflections.rb +15 -0
- data/spec/rails-4.0/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/rails-4.0/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/rails-4.0/app_root/config/initializers/session_store.rb +8 -0
- data/spec/rails-4.0/app_root/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails-4.0/app_root/config/locales/en.yml +5 -0
- data/spec/rails-4.0/app_root/config/routes.rb +2 -0
- data/spec/rails-4.0/app_root/db/test.sqlite3 +0 -0
- data/spec/rails-4.0/app_root/log/development.log +50 -0
- data/spec/rails-4.0/app_root/log/test.log +2399 -0
- data/spec/rails-4.0/app_root/public/404.html +26 -0
- data/spec/rails-4.0/app_root/public/422.html +26 -0
- data/spec/rails-4.0/app_root/public/500.html +25 -0
- data/spec/rails-4.0/app_root/public/favicon.ico +0 -0
- data/spec/rails-4.0/app_root/script/rails +6 -0
- data/spec/rails-4.0/spec/spec_helper.rb +25 -0
- data/spec/shared/cardiac/declarations_spec.rb +103 -0
- data/spec/shared/cardiac/model/base_spec.rb +446 -0
- data/spec/shared/cardiac/operation_builder_spec.rb +96 -0
- data/spec/shared/cardiac/operation_handler_spec.rb +82 -0
- data/spec/shared/cardiac/representation/reflection_spec.rb +73 -0
- data/spec/shared/cardiac/resource/adapter_spec.rb +83 -0
- data/spec/shared/cardiac/resource/builder_spec.rb +52 -0
- data/spec/shared/cardiac/resource/codec_methods_spec.rb +63 -0
- data/spec/shared/cardiac/resource/config_methods_spec.rb +52 -0
- data/spec/shared/cardiac/resource/extension_methods_spec.rb +215 -0
- data/spec/shared/cardiac/resource/request_methods_spec.rb +186 -0
- data/spec/shared/cardiac/resource/uri_methods_spec.rb +212 -0
- data/spec/shared/support/client_execution.rb +28 -0
- data/spec/spec_helper.rb +24 -0
- metadata +463 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
module Cardiac
|
2
|
+
|
3
|
+
# If a subresource has any path or query values, it will treat the base resource's path
|
4
|
+
# as being a *directory*.
|
5
|
+
class Subresource < Resource
|
6
|
+
|
7
|
+
begin
|
8
|
+
remove_method :subresource, :subresources_values
|
9
|
+
private :subresources_values=
|
10
|
+
rescue
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(base)
|
14
|
+
@_base_resource = base
|
15
|
+
super base.base_value
|
16
|
+
self.method_value = base.method_value
|
17
|
+
end
|
18
|
+
|
19
|
+
# Overridden to enforce the appending of subresource paths.
|
20
|
+
def base_value
|
21
|
+
base_path = @_base_resource.build_path
|
22
|
+
base_path+='/' if @path_values.any? && base_path[-1]!='/'
|
23
|
+
super.merge URI::Generic.build(path: base_path)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Lazily builds the parent module, then redefines this method as an attr_reader.
|
27
|
+
def __parent_module__
|
28
|
+
build_parent_module
|
29
|
+
ensure
|
30
|
+
singleton_class.class_eval "attr_reader :__parent_module__"
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def build_scheme
|
36
|
+
@https_value.nil? ? @_base_resource.build_scheme : super
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_host
|
40
|
+
@host_value.nil? ? @_base_resource.build_host : super
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_port(scheme=nil)
|
44
|
+
@port_value.nil? ? @_base_resource.build_port(scheme) : super
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_query_values
|
48
|
+
@_base_resource.build_query_values + @query_values.select(&:present?)
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_options(base_options=nil)
|
52
|
+
super @_base_resource.send(:build_options, base_options||{})
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_headers(base_headers=nil)
|
56
|
+
super @_base_resource.send(:build_headers, base_headers||{})
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_config(base_config=nil)
|
60
|
+
super @_base_resource.send(:build_config, base_config||{})
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_parent_module
|
64
|
+
@__parent_module__ ||= Module.new.tap{|x| @_base_resource.build_extensions_for_module x }
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_subresources_for_module mod
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_encoder(*previous)
|
72
|
+
result = @_base_resource.send :build_encoder, *previous
|
73
|
+
result[0] = encoder_search_value || result[0]
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Overridden to omit any subresources.
|
80
|
+
def apply_extensions_to_module! mod=Module.new
|
81
|
+
mod.send :include, build_parent_module
|
82
|
+
build_extensions_for_module mod
|
83
|
+
build_operations_for_module mod
|
84
|
+
mod
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'active_support/core_ext/hash/deep_merge'
|
3
|
+
|
4
|
+
module Cardiac
|
5
|
+
module UriMethods
|
6
|
+
|
7
|
+
# Scheme selection.
|
8
|
+
def ssl(z) self.https_value = z.nil? ? z : !!z; self end
|
9
|
+
def http() ssl(false) end
|
10
|
+
def https() ssl(true) end
|
11
|
+
def scheme(word)
|
12
|
+
case word
|
13
|
+
when /^http$/i then http
|
14
|
+
when /^https$/i then https
|
15
|
+
when NilClass then ssl(nil)
|
16
|
+
else raise ArgumentError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# HTTP method selection.
|
21
|
+
def http_method(k) self.method_value = k.to_s.downcase.to_sym ; self end
|
22
|
+
|
23
|
+
# Userinfo selection.
|
24
|
+
def user(s) self.user_value = s ; self end
|
25
|
+
def password(s) self.password_value = s ; self end
|
26
|
+
def userinfo(userinfo)
|
27
|
+
if userinfo
|
28
|
+
self.user_value, _password = userinfo.split(':',2)
|
29
|
+
self.password_value ||= _password
|
30
|
+
else
|
31
|
+
self.user_value = self.password_value = nil
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Host and port selection.
|
37
|
+
def host(s) self.host_value = s ; self end
|
38
|
+
|
39
|
+
def port(n) self.port_value = n ; self end
|
40
|
+
|
41
|
+
# Path selection.
|
42
|
+
def path(s,*rest) self.path_values += check_paths(rest.unshift(s)) ; self end
|
43
|
+
|
44
|
+
# Query parameter selection.
|
45
|
+
def query(q,*rest) self.query_values += check_queries(rest.unshift(q)) ; self end
|
46
|
+
def reset_query(*q) query_values.replace check_queries(q) ; self end
|
47
|
+
|
48
|
+
# Relative resource selection.
|
49
|
+
def at(rel)
|
50
|
+
apply_uri_components! check_at(rel) if rel.present?
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Convert this resource into a URI.
|
55
|
+
def to_uri
|
56
|
+
build_uri
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convert this resource into an URL string.
|
60
|
+
def to_url
|
61
|
+
to_uri.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
# Convert this resource into a relative URI.
|
65
|
+
def to_relative_uri
|
66
|
+
build_uri.route_from base_value
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert this resource into a relative URL string
|
70
|
+
def to_relative_url
|
71
|
+
to_relative_uri.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
# Derives the userinfo from the user_value and password_value.
|
77
|
+
def build_userinfo
|
78
|
+
@password_value ? "#{@user_value}:#{@password_value}" : @user_value if @user_value
|
79
|
+
end
|
80
|
+
|
81
|
+
# Derives the scheme from the base_value and https_value.
|
82
|
+
def build_scheme
|
83
|
+
case @https_value
|
84
|
+
when FalseClass then 'http'
|
85
|
+
when NilClass then base_value.scheme || 'http'
|
86
|
+
else 'https'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Derives the path from the base_value and paths_value.
|
91
|
+
def build_path
|
92
|
+
@path_values.inject(base_value){|uri,path| uri.merge URI::Generic.build(path: path) }.path
|
93
|
+
end
|
94
|
+
|
95
|
+
# Needed by Subresource
|
96
|
+
def build_query_values
|
97
|
+
@query_values.select(&:present?)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Derives the query from the built-up query_values.
|
101
|
+
def build_query
|
102
|
+
case query = build_query_values.inject{|q,v| query_coder.decode(q).deep_merge(query_coder.decode(v)) }
|
103
|
+
when Hash
|
104
|
+
query_coder.encode(query)
|
105
|
+
when String, NilClass
|
106
|
+
query
|
107
|
+
else
|
108
|
+
raise InvalidRepresentationError, 'expected Hash, String, or NilClass but got: '+query.class.name
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Derives a URI from the built-up values in this resource.
|
113
|
+
def build_uri
|
114
|
+
# Validate the scheme.
|
115
|
+
scheme = build_scheme.upcase
|
116
|
+
raise UnresolvableResourceError, 'scheme must be http or https' if scheme!='HTTP' && scheme!='HTTPS'
|
117
|
+
|
118
|
+
# Build and normalize the URI.
|
119
|
+
URI.const_get(scheme).
|
120
|
+
build2(host: build_host, port: build_port(scheme), userinfo: build_userinfo, path: build_path, query: build_query).
|
121
|
+
normalize
|
122
|
+
end
|
123
|
+
|
124
|
+
# Derives a host from the built-up values in this resource, requiring it to be present.
|
125
|
+
def build_host
|
126
|
+
host = @host_value || base_value.host
|
127
|
+
raise UnresolvableResourceError, 'no HTTP host specified' if host.blank?
|
128
|
+
host
|
129
|
+
end
|
130
|
+
|
131
|
+
# Derives a port from the built-up values in this resource, but omits default ports.
|
132
|
+
def build_port(scheme=nil)
|
133
|
+
non_default_port(scheme, @port_value || non_default_port(base_value.scheme, base_value.port))
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
# Returns nil if the given port is the default for the scheme, otherwise returns the port itself.
|
139
|
+
def non_default_port(scheme,port)
|
140
|
+
case scheme
|
141
|
+
when /^http$/i then port==80 ? nil : port
|
142
|
+
when /^https$/i then port==443 ? nil : port
|
143
|
+
else port
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def query_coder
|
148
|
+
::Cardiac::Representation::Codecs::UrlEncoded
|
149
|
+
end
|
150
|
+
|
151
|
+
def apply_uri_components! other
|
152
|
+
other, base = to_uri.coerce(other)
|
153
|
+
base.component.each do |part|
|
154
|
+
next if part == :fragment
|
155
|
+
value = other.send(part)
|
156
|
+
send(part, value) if base.merge!(URI::Generic.build(part => value))
|
157
|
+
end
|
158
|
+
base
|
159
|
+
end
|
160
|
+
|
161
|
+
def check_at(rel)
|
162
|
+
raise ArgumentError unless String===rel or URI===rel
|
163
|
+
rel
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_paths(paths)
|
167
|
+
raise ArgumentError unless paths.all?{|s| String===s }
|
168
|
+
paths
|
169
|
+
end
|
170
|
+
|
171
|
+
def check_queries(queries)
|
172
|
+
raise ArgumentError unless queries.all?{|q| String===q || Hash===q }
|
173
|
+
queries
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_support/core_ext/array/wrap'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
|
4
|
+
module Cardiac
|
5
|
+
class Resource
|
6
|
+
|
7
|
+
attr_accessor :base_value,
|
8
|
+
|
9
|
+
# built by: UriMethods
|
10
|
+
:https_value, :user_value, :password_value,
|
11
|
+
:host_value, :port_value, :path_values, :query_values,
|
12
|
+
|
13
|
+
# built by: RequestMethods
|
14
|
+
:method_value, :headers_values, :options_values, :accepts_values,
|
15
|
+
|
16
|
+
# built by: CodecMethods
|
17
|
+
:decoders_values, :encoder_search_value, :encoder_handler_value,
|
18
|
+
|
19
|
+
# built by: ExtensionMethods
|
20
|
+
:extensions_values, :operations_values, :subresources_values
|
21
|
+
|
22
|
+
include UriMethods
|
23
|
+
include RequestMethods
|
24
|
+
include CodecMethods
|
25
|
+
include ExtensionMethods
|
26
|
+
include ConfigMethods
|
27
|
+
|
28
|
+
# Configure our allowed builder methods.
|
29
|
+
self.allowed_builder_methods = [
|
30
|
+
:scheme, :ssl, :https, :user, :password, :host, :port, :path, :query, :reset_query,
|
31
|
+
:http_method, :headers, :header, :reset_headers, :options, :option, :reset_options,
|
32
|
+
:decoders, :encoder, :extending
|
33
|
+
].freeze
|
34
|
+
|
35
|
+
def initialize(base)
|
36
|
+
self.base_value = ::URI::Generic===base ? base.dup : URI(base) # compatibility with 1.9.2 and below
|
37
|
+
self.path_values = []
|
38
|
+
self.query_values = []
|
39
|
+
self.headers_values = []
|
40
|
+
self.accepts_values = []
|
41
|
+
self.options_values = []
|
42
|
+
self.decoders_values = []
|
43
|
+
self.operations_values = []
|
44
|
+
self.subresources_values = []
|
45
|
+
self.extensions_values = []
|
46
|
+
extract_from_base!
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_resource
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
to_url
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_reflection(verb=method_value)
|
58
|
+
ResourceReflection.new(to_resource, verb)
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
# Builds client options, without a payload.
|
64
|
+
def build_client_options(verb=build_http_method)
|
65
|
+
build_options.merge method: verb, url: to_url, headers: build_headers
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Separate userinfo and query from the base_value.
|
71
|
+
def extract_from_base!(base=@base_value)
|
72
|
+
self.user_value, self.password_value = base.user, base.password if base.userinfo
|
73
|
+
query_values << base.query if base.query
|
74
|
+
base.userinfo = base.query = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/cardiac/util.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
require 'active_support/builder'
|
3
|
+
require 'blankslate'
|
4
|
+
|
5
|
+
module Cardiac
|
6
|
+
|
7
|
+
# Specialized version(s) of Rack utility functions.
|
8
|
+
module RackUtils
|
9
|
+
include ::Rack::Utils
|
10
|
+
|
11
|
+
# Overridden to work with false, true and nil, and otherwise call :to_param on single values that are not strings.
|
12
|
+
def build_nested_query(value,prefix=nil)
|
13
|
+
case value
|
14
|
+
when Hash, Array, String
|
15
|
+
super
|
16
|
+
when TrueClass
|
17
|
+
"#{prefix}=true"
|
18
|
+
when FalseClass
|
19
|
+
"#{prefix}=false"
|
20
|
+
when NilClass
|
21
|
+
"#{prefix}="
|
22
|
+
else
|
23
|
+
super value.to_param, prefix
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Base proxy class.
|
29
|
+
#
|
30
|
+
# On Ruby 2.0 and above, we can use ActiveSupport's ProxyObject, otherwise we must use BlankSlate.
|
31
|
+
class Proxy < (RUBY_VERSION>='2.0' && defined?(::ActiveSupport::ProxyObject) ? ::ActiveSupport::ProxyObject : ::BlankSlate)
|
32
|
+
|
33
|
+
# We at least would like some basics here.
|
34
|
+
%w(kind_of? is_a? inspect class method send).each do |k|
|
35
|
+
define_method k, ::Kernel.instance_method(k)
|
36
|
+
end
|
37
|
+
%w(class extend send object_id public_send).each do |k|
|
38
|
+
define_method :"__#{k}__", ::Kernel.instance_method(k)
|
39
|
+
end
|
40
|
+
|
41
|
+
# By default, do not advertise methods on this proxy object.
|
42
|
+
def respond_to?(name,include_all=false)
|
43
|
+
respond_to_missing?(name,include_all)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# A specialized proxy class for building extension modules while supporting method_missing.
|
48
|
+
class ExtensionBuilder < Proxy
|
49
|
+
def initialize(builder,extension_module=nil)
|
50
|
+
@builder = builder
|
51
|
+
@extension_module = extension_module
|
52
|
+
end
|
53
|
+
|
54
|
+
# Performs a :module_eval on the extension module,
|
55
|
+
# temporarily defining :method_missing on it's singleton class for the duration of the block.
|
56
|
+
def extension_eval(&block)
|
57
|
+
extension_exec(&block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Performs a :module_eval or :module_exec on the extension module,
|
61
|
+
# temporarily defining :method_missing on it's singleton class for the duration of the block.
|
62
|
+
def extension_exec(*args, &block)
|
63
|
+
orig_missing = build_method_missing
|
64
|
+
begin
|
65
|
+
args.empty? ? __extension_module__.module_eval(&block) : __extension_module__.module_exec(*args,&block)
|
66
|
+
ensure
|
67
|
+
__extension_module__.singleton_class.send(:remove_method, :method_missing)
|
68
|
+
__extension_module__.define_singleton_method :method_missing, orig_missing if orig_missing
|
69
|
+
end
|
70
|
+
@builder
|
71
|
+
end
|
72
|
+
|
73
|
+
# Lazily creates the extension module.
|
74
|
+
def __extension_module__
|
75
|
+
@extension_module ||= ::Module.new
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
# Called to build a :method_missing implementation on the extension module.
|
81
|
+
def build_method_missing
|
82
|
+
if __extension_module__.singleton_methods(false).include?(:method_missing)
|
83
|
+
orig = __extension_module__.method(:method_missing)
|
84
|
+
end
|
85
|
+
self.class.build_method_missing(self)
|
86
|
+
orig
|
87
|
+
end
|
88
|
+
|
89
|
+
# This method's arguments are minimal closure needed to build :method_missing.
|
90
|
+
# The instance version of this method delegates here.
|
91
|
+
def self.build_method_missing(adapter)
|
92
|
+
# NOTE: Your editor may not like the |name,*args,&block| syntax below. Ignore any errors it reports.
|
93
|
+
adapter.__extension_module__.define_singleton_method :method_missing do |name,*args,&block|
|
94
|
+
if adapter.respond_to?(name)
|
95
|
+
adapter.__send__(name, *args, &block)
|
96
|
+
else
|
97
|
+
super(name, *args, &block)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Delegates to the builder.
|
105
|
+
def respond_to_missing?(name,include_private=false)
|
106
|
+
@builder.respond_to?(name, include_private)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Delegates to the builder, saves it's return value as the new builder, and returns self.
|
110
|
+
def method_missing(name, *args, &block)
|
111
|
+
if @builder.respond_to?(name)
|
112
|
+
@builder = @builder.__send__(name, *args, &block)
|
113
|
+
self
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
data/lib/cardiac.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/dependencies/autoload'
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
|
7
|
+
require 'cardiac/version'
|
8
|
+
require 'cardiac/errors'
|
9
|
+
|
10
|
+
module Cardiac
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
|
13
|
+
# Utility classes.
|
14
|
+
autoload_at 'cardiac/util' do
|
15
|
+
autoload :RackUtils
|
16
|
+
autoload :Proxy
|
17
|
+
autoload :ExtensionBuilder
|
18
|
+
autoload :Mixin
|
19
|
+
end
|
20
|
+
|
21
|
+
# Resources and builder DSL.
|
22
|
+
autoload :Resource
|
23
|
+
autoload_under 'resource' do
|
24
|
+
autoload :Subresource
|
25
|
+
autoload :UriMethods
|
26
|
+
autoload :RequestMethods
|
27
|
+
autoload :CodecMethods
|
28
|
+
autoload :ExtensionMethods
|
29
|
+
autoload :ConfigMethods
|
30
|
+
autoload :ResourceBuilder, 'cardiac/resource/builder'
|
31
|
+
autoload :ResourceAdapter, 'cardiac/resource/adapter'
|
32
|
+
end
|
33
|
+
autoload :Representation
|
34
|
+
autoload_at 'cardiac/declarations' do
|
35
|
+
autoload :DeclarationMethods
|
36
|
+
autoload :DeclarationBuilder
|
37
|
+
autoload :Declarations
|
38
|
+
end
|
39
|
+
|
40
|
+
# Operations and execution.
|
41
|
+
autoload :OperationHandler
|
42
|
+
autoload :OperationResult, 'cardiac/operation_handler'
|
43
|
+
autoload :OperationBuilder
|
44
|
+
autoload :OperationProxy, 'cardiac/operation_builder'
|
45
|
+
autoload :LogSubscriber
|
46
|
+
|
47
|
+
# Reflections, models and definitions.
|
48
|
+
autoload_at 'cardiac/reflections' do
|
49
|
+
autoload :BaseReflection
|
50
|
+
autoload :ChainReflection
|
51
|
+
autoload :ResourceReflection
|
52
|
+
autoload :OperationReflection
|
53
|
+
end
|
54
|
+
autoload :Model
|
55
|
+
end
|
56
|
+
|
57
|
+
require 'cardiac/railtie' if defined? ::Rails
|
58
|
+
|
59
|
+
ActiveSupport.on_load(:i18n) do
|
60
|
+
I18n.load_path << File.dirname(__FILE__) + '/cardiac/model/locale/en.yml'
|
61
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../..
|
3
|
+
specs:
|
4
|
+
cardiac (0.2.0.pre1)
|
5
|
+
active_attr (>= 0.8.2)
|
6
|
+
activemodel (>= 3.2, < 4.1)
|
7
|
+
activesupport (>= 3.2, < 4.1)
|
8
|
+
i18n (~> 0.6, >= 0.6.4)
|
9
|
+
json (> 1.8.0)
|
10
|
+
mime-types (> 1.1)
|
11
|
+
multi_json (~> 1.0)
|
12
|
+
rack (>= 1.4.5)
|
13
|
+
rack-cache (~> 1.2)
|
14
|
+
rack-client (~> 0.4.2)
|
15
|
+
|
16
|
+
GEM
|
17
|
+
remote: http://rubygems.org/
|
18
|
+
specs:
|
19
|
+
actionmailer (3.2.19)
|
20
|
+
actionpack (= 3.2.19)
|
21
|
+
mail (~> 2.5.4)
|
22
|
+
actionpack (3.2.19)
|
23
|
+
activemodel (= 3.2.19)
|
24
|
+
activesupport (= 3.2.19)
|
25
|
+
builder (~> 3.0.0)
|
26
|
+
erubis (~> 2.7.0)
|
27
|
+
journey (~> 1.0.4)
|
28
|
+
rack (~> 1.4.5)
|
29
|
+
rack-cache (~> 1.2)
|
30
|
+
rack-test (~> 0.6.1)
|
31
|
+
sprockets (~> 2.2.1)
|
32
|
+
active_attr (0.8.4)
|
33
|
+
activemodel (>= 3.0.2, < 4.2)
|
34
|
+
activesupport (>= 3.0.2, < 4.2)
|
35
|
+
activemodel (3.2.19)
|
36
|
+
activesupport (= 3.2.19)
|
37
|
+
builder (~> 3.0.0)
|
38
|
+
activerecord (3.2.19)
|
39
|
+
activemodel (= 3.2.19)
|
40
|
+
activesupport (= 3.2.19)
|
41
|
+
arel (~> 3.0.2)
|
42
|
+
tzinfo (~> 0.3.29)
|
43
|
+
activeresource (3.2.19)
|
44
|
+
activemodel (= 3.2.19)
|
45
|
+
activesupport (= 3.2.19)
|
46
|
+
activesupport (3.2.19)
|
47
|
+
i18n (~> 0.6, >= 0.6.4)
|
48
|
+
multi_json (~> 1.0)
|
49
|
+
arel (3.0.3)
|
50
|
+
awesome_print (1.2.0)
|
51
|
+
builder (3.0.4)
|
52
|
+
diff-lcs (1.2.5)
|
53
|
+
erubis (2.7.0)
|
54
|
+
hike (1.2.3)
|
55
|
+
i18n (0.6.11)
|
56
|
+
journey (1.0.4)
|
57
|
+
json (1.8.1)
|
58
|
+
mail (2.5.4)
|
59
|
+
mime-types (~> 1.16)
|
60
|
+
treetop (~> 1.4.8)
|
61
|
+
mime-types (1.25.1)
|
62
|
+
multi_json (1.10.1)
|
63
|
+
polyglot (0.3.5)
|
64
|
+
rack (1.4.5)
|
65
|
+
rack-cache (1.2)
|
66
|
+
rack (>= 0.4)
|
67
|
+
rack-client (0.4.2)
|
68
|
+
rack (>= 1.0.0)
|
69
|
+
rack-ssl (1.3.4)
|
70
|
+
rack
|
71
|
+
rack-test (0.6.2)
|
72
|
+
rack (>= 1.0)
|
73
|
+
rails (3.2.19)
|
74
|
+
actionmailer (= 3.2.19)
|
75
|
+
actionpack (= 3.2.19)
|
76
|
+
activerecord (= 3.2.19)
|
77
|
+
activeresource (= 3.2.19)
|
78
|
+
activesupport (= 3.2.19)
|
79
|
+
bundler (~> 1.0)
|
80
|
+
railties (= 3.2.19)
|
81
|
+
railties (3.2.19)
|
82
|
+
actionpack (= 3.2.19)
|
83
|
+
activesupport (= 3.2.19)
|
84
|
+
rack-ssl (~> 1.3.2)
|
85
|
+
rake (>= 0.8.7)
|
86
|
+
rdoc (~> 3.4)
|
87
|
+
thor (>= 0.14.6, < 2.0)
|
88
|
+
rake (10.3.2)
|
89
|
+
rdoc (3.12.2)
|
90
|
+
json (~> 1.4)
|
91
|
+
rspec (3.0.0)
|
92
|
+
rspec-core (~> 3.0.0)
|
93
|
+
rspec-expectations (~> 3.0.0)
|
94
|
+
rspec-mocks (~> 3.0.0)
|
95
|
+
rspec-collection_matchers (1.0.0)
|
96
|
+
rspec-expectations (>= 2.99.0.beta1)
|
97
|
+
rspec-core (3.0.4)
|
98
|
+
rspec-support (~> 3.0.0)
|
99
|
+
rspec-expectations (3.0.4)
|
100
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
101
|
+
rspec-support (~> 3.0.0)
|
102
|
+
rspec-mocks (3.0.4)
|
103
|
+
rspec-support (~> 3.0.0)
|
104
|
+
rspec-rails (3.0.2)
|
105
|
+
actionpack (>= 3.0)
|
106
|
+
activesupport (>= 3.0)
|
107
|
+
railties (>= 3.0)
|
108
|
+
rspec-core (~> 3.0.0)
|
109
|
+
rspec-expectations (~> 3.0.0)
|
110
|
+
rspec-mocks (~> 3.0.0)
|
111
|
+
rspec-support (~> 3.0.0)
|
112
|
+
rspec-support (3.0.4)
|
113
|
+
sprockets (2.2.2)
|
114
|
+
hike (~> 1.2)
|
115
|
+
multi_json (~> 1.0)
|
116
|
+
rack (~> 1.0)
|
117
|
+
tilt (~> 1.1, != 1.3.0)
|
118
|
+
sqlite3 (1.3.9)
|
119
|
+
thor (0.19.1)
|
120
|
+
tilt (1.4.1)
|
121
|
+
treetop (1.4.15)
|
122
|
+
polyglot
|
123
|
+
polyglot (>= 0.3.1)
|
124
|
+
tzinfo (0.3.41)
|
125
|
+
|
126
|
+
PLATFORMS
|
127
|
+
ruby
|
128
|
+
|
129
|
+
DEPENDENCIES
|
130
|
+
awesome_print
|
131
|
+
cardiac!
|
132
|
+
rails (~> 3.2, < 4.0)
|
133
|
+
rspec (~> 3.0, < 3.1)
|
134
|
+
rspec-collection_matchers (~> 1.0)
|
135
|
+
rspec-rails (~> 3.0, < 3.1)
|
136
|
+
sqlite3
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc 'Default: Run all specs for rails 3.2'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc 'Run all specs for rails 3.2'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.pattern = defined?(SPEC) ? SPEC : ['**/*_spec.rb', '../shared/**/*_spec.rb']
|
10
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// the compiled file.
|
9
|
+
//
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
12
|
+
//
|
13
|
+
//= require jquery
|
14
|
+
//= require jquery_ujs
|
15
|
+
//= require_tree .
|