cardiac 0.2.0.pre2
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.
- 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 .
|