smooth 2.0.1 → 2.0.2
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 +4 -4
- data/.gitignore +7 -0
- data/Gemfile +1 -2
- data/README.md +150 -5
- data/Rakefile +16 -0
- data/app/assets/javascripts/smooth/index.js +5152 -0
- data/bin/smooth +9 -0
- data/{app/assets/javascripts/smooth → developer-tools}/.keep +0 -0
- data/developer-tools/bower.json +8 -0
- data/developer-tools/config.ru +3 -0
- data/developer-tools/dist/08d606864d3ad3f0b98660d391f5a1c2.gif +0 -0
- data/developer-tools/dist/2d66bcdc27cd89f71068e98a7a929712.gif +0 -0
- data/developer-tools/dist/3e9816417b11485d454f9b3662b06e7b.eot +0 -0
- data/developer-tools/dist/47de617fd1d745ad120ccb9e2924b98c.gif +0 -0
- data/developer-tools/dist/5ae23ad29b67289a1375d2043e289c52.eot +0 -0
- data/developer-tools/dist/60c2a8500e63bf211b7df9608f7613ea.svg +450 -0
- data/developer-tools/dist/645f50ba6c1e56f078fa018855d97eb0.gif +0 -0
- data/developer-tools/dist/71ab514d1cedda303417ad7a06472fea.ttf +0 -0
- data/developer-tools/dist/8cca2f02b0af2da365ff4d1755f29146.ttf +0 -0
- data/developer-tools/dist/939cf252f0eb4efbd2d170c974411c49.gif +0 -0
- data/developer-tools/dist/9af25aaeb6ca6d08d213b04841813eb5.gif +0 -0
- data/developer-tools/dist/b683029bafe0305ac2234038a03e1541.woff +0 -0
- data/developer-tools/dist/c9dec22105ad9330c811599b8b6464f8.woff +0 -0
- data/developer-tools/dist/ca279c55a51ab2641c4712a333633581.gif +0 -0
- data/developer-tools/dist/client.js +5152 -0
- data/developer-tools/dist/f5b27137d3f5e9b1d91b16b37386dd03.gif +0 -0
- data/developer-tools/dist/f99a231ed57ee113b50b1c3e9f9fcdc3.svg +399 -0
- data/developer-tools/dist/index.html +18 -0
- data/developer-tools/dist/inspector.js +38432 -0
- data/developer-tools/dist/jquery.min.js +9190 -0
- data/developer-tools/package.json +39 -0
- data/developer-tools/server.js +14 -0
- data/developer-tools/src/client.coffee +21 -0
- data/developer-tools/src/client/collection.coffee +14 -0
- data/developer-tools/src/client/model.coffee +11 -0
- data/developer-tools/src/client/resource.coffee +132 -0
- data/{app/controllers/.keep → developer-tools/src/client/runner.coffee} +0 -0
- data/developer-tools/src/dependencies.coffee +7 -0
- data/developer-tools/src/inspector.cjsx +49 -0
- data/developer-tools/src/inspector/models/interface_collection.coffee +31 -0
- data/developer-tools/src/inspector/pages/index.cjsx +31 -0
- data/developer-tools/src/inspector/pages/resources.cjsx +5 -0
- data/developer-tools/src/inspector/views/grid_sort.cjsx +23 -0
- data/developer-tools/src/inspector/views/icon_heading.cjsx +15 -0
- data/developer-tools/src/inspector/views/resource_card.cjsx +34 -0
- data/developer-tools/src/inspector/views/sidebar.cjsx +12 -0
- data/developer-tools/src/inspector/views/toolbar.cjsx +17 -0
- data/developer-tools/src/styles/index.scss +136 -0
- data/developer-tools/src/styles/views.scss +13 -0
- data/developer-tools/src/util.coffee +48 -0
- data/developer-tools/webpack.config.js +56 -0
- data/developer-tools/webpack.hot.config.js +65 -0
- data/lib/smooth.rb +209 -28
- data/lib/smooth/active_record/adapter.rb +24 -0
- data/lib/smooth/api.rb +272 -18
- data/lib/smooth/api/policy.rb +2 -2
- data/lib/smooth/api/tracking.rb +4 -4
- data/lib/smooth/application.rb +66 -0
- data/lib/smooth/cache.rb +1 -1
- data/lib/smooth/command.rb +267 -18
- data/lib/smooth/command/async_worker.rb +27 -0
- data/lib/smooth/command/instrumented.rb +6 -4
- data/lib/smooth/command/run_proxy.rb +21 -0
- data/lib/smooth/configuration.rb +63 -8
- data/lib/smooth/documentation.rb +3 -6
- data/lib/smooth/dsl.rb +1 -36
- data/lib/smooth/dsl_adapter.rb +34 -0
- data/lib/smooth/event.rb +8 -4
- data/lib/smooth/event/proxy.rb +9 -0
- data/lib/smooth/event/relay.rb +38 -0
- data/lib/smooth/example.rb +1 -1
- data/lib/smooth/ext/core.rb +16 -0
- data/lib/smooth/model_adapter.rb +31 -0
- data/lib/smooth/query.rb +143 -13
- data/lib/smooth/resource.rb +227 -52
- data/lib/smooth/resource/router.rb +217 -0
- data/lib/smooth/resource/templating.rb +62 -0
- data/lib/smooth/resource/tracking.rb +1 -1
- data/lib/smooth/response.rb +73 -0
- data/lib/smooth/serializer.rb +102 -11
- data/lib/smooth/user_adapter.rb +83 -0
- data/lib/smooth/util.rb +17 -0
- data/lib/smooth/version.rb +1 -1
- data/smooth.gemspec +6 -2
- data/spec/acceptance/books_routes_spec.rb +50 -0
- data/spec/acceptance/embedded_relationships_spec.rb +26 -0
- data/spec/dummy/app/apis/application_api.rb +8 -3
- data/spec/dummy/app/commands/create_book.rb +5 -0
- data/spec/dummy/app/models/book.rb +1 -0
- data/spec/dummy/app/models/library.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/app/queries/book_query.rb +13 -0
- data/spec/dummy/app/resources/{books.rb → books_definition.rb} +37 -12
- data/spec/dummy/db/migrate/20140824215902_create_users.rb +10 -0
- data/spec/dummy/db/migrate/20140826193259_create_libraries.rb +10 -0
- data/spec/dummy/db/schema.rb +8 -1
- data/spec/lib/smooth/api/async_spec.rb +21 -0
- data/spec/lib/smooth/api_spec.rb +8 -0
- data/spec/lib/smooth/command_spec.rb +87 -6
- data/spec/lib/smooth/configuration_spec.rb +4 -0
- data/spec/lib/smooth/event/relay_spec.rb +33 -0
- data/spec/lib/smooth/event_spec.rb +5 -8
- data/spec/lib/smooth/query_spec.rb +42 -0
- data/spec/lib/smooth/resource/router_spec.rb +14 -0
- data/spec/lib/smooth/resource_spec.rb +33 -1
- data/spec/lib/smooth/serializer_spec.rb +20 -0
- data/spec/lib/smooth/templating_spec.rb +23 -0
- data/spec/lib/smooth/util_spec.rb +22 -0
- data/spec/spec_helper.rb +1 -1
- metadata +151 -17
- data/app/helpers/.keep +0 -0
- data/app/mailers/.keep +0 -0
- data/app/models/.keep +0 -0
- data/app/views/.keep +0 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
module Smooth
|
|
2
|
+
class Resource
|
|
3
|
+
class Router
|
|
4
|
+
attr_reader :resource,
|
|
5
|
+
:table,
|
|
6
|
+
:descriptions,
|
|
7
|
+
:rules
|
|
8
|
+
|
|
9
|
+
def initialize(resource, _options = {})
|
|
10
|
+
@resource = resource
|
|
11
|
+
@table = {}
|
|
12
|
+
@descriptions = {}
|
|
13
|
+
@rules = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# I may be getting this in a convoluted way
|
|
17
|
+
# may be easier to build up naturally
|
|
18
|
+
def interface_documentation
|
|
19
|
+
descriptions.keys.reduce({}) do |memo, verb|
|
|
20
|
+
routes = descriptions[verb]
|
|
21
|
+
routes.each do |_|
|
|
22
|
+
pattern, description = _
|
|
23
|
+
memo["#{ verb.to_s.upcase } #{ pattern }"] = description
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
memo
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def route_table
|
|
31
|
+
@route_table ||= route_patterns_table.reduce({}) do |memo, p|
|
|
32
|
+
route_name, details = p
|
|
33
|
+
memo[route_name] = details[:pattern]
|
|
34
|
+
memo
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def expand_routes(from_attributes = {})
|
|
39
|
+
route_patterns_table.reduce({}) do |memo, p|
|
|
40
|
+
route_name, details = p
|
|
41
|
+
memo[route_name] = Smooth.util.expand_url_template(details[:template], from_attributes)
|
|
42
|
+
memo
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def route_patterns_table
|
|
47
|
+
return @route_patterns_table if @route_patterns_table
|
|
48
|
+
|
|
49
|
+
@route_patterns_table = rules.flatten.compact.reduce({}) do |memo, rule|
|
|
50
|
+
memo.tap do
|
|
51
|
+
name = rule[:name]
|
|
52
|
+
pattern = rule[:pattern]
|
|
53
|
+
template = rule[:template]
|
|
54
|
+
|
|
55
|
+
memo[name] = {
|
|
56
|
+
pattern: pattern,
|
|
57
|
+
template: template,
|
|
58
|
+
variables: Array(template.variables)
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def patterns
|
|
65
|
+
rules.flatten.compact.map { |r| r.fetch(:pattern) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def uri_templates
|
|
69
|
+
rules.flatten.compact.map { |r| r.fetch(:template) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def apply_to(sinatra)
|
|
73
|
+
router = self
|
|
74
|
+
|
|
75
|
+
user_finder = resource.api.method(:lookup_current_user).to_proc
|
|
76
|
+
policy_finder = resource.api.method(:lookup_policy).to_proc
|
|
77
|
+
|
|
78
|
+
router.rules.each do |_|
|
|
79
|
+
options, _ = _
|
|
80
|
+
|
|
81
|
+
handler = methods_table.method(options[:name])
|
|
82
|
+
|
|
83
|
+
sinatra.send options[:method], options[:pattern] do |*args|
|
|
84
|
+
begin
|
|
85
|
+
request = {
|
|
86
|
+
headers: headers,
|
|
87
|
+
params: params,
|
|
88
|
+
user: user_finder.call(params, headers),
|
|
89
|
+
policy: policy_finder.call(params, headers),
|
|
90
|
+
args: args
|
|
91
|
+
}
|
|
92
|
+
rescue => exception
|
|
93
|
+
halt 500, {}, { error: exception.message, backtrace: exception.backtrace, stage: 'request' }.to_json
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
response = handler.call(request.to_mash)
|
|
98
|
+
|
|
99
|
+
body response.body
|
|
100
|
+
headers response.headers
|
|
101
|
+
status response.status
|
|
102
|
+
rescue => exception
|
|
103
|
+
halt 500, {}, { error: exception.message, backtrace: exception.backtrace, stage: 'response' }.to_json
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def build_methods_table
|
|
110
|
+
router = self
|
|
111
|
+
|
|
112
|
+
@methods_table_class = Class.new do
|
|
113
|
+
|
|
114
|
+
k = self
|
|
115
|
+
|
|
116
|
+
router.rules.each do |_|
|
|
117
|
+
options, block = _
|
|
118
|
+
method_name = options.fetch(:name)
|
|
119
|
+
k.send :define_method, method_name, (block || router.lookup_handler_for(options[:method], options[:to]))
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def counter
|
|
125
|
+
@counter ||= 0
|
|
126
|
+
@counter += 1
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def methods_table
|
|
130
|
+
@methods_table ||= (@methods_table_class || build_methods_table).new
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def desc(description, *_args)
|
|
134
|
+
descriptions[:current] = description
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Verbs = {
|
|
138
|
+
get: :get,
|
|
139
|
+
show: :get,
|
|
140
|
+
put: :put,
|
|
141
|
+
patch: :put,
|
|
142
|
+
create: :post,
|
|
143
|
+
delete: :delete,
|
|
144
|
+
destroy: :destroy,
|
|
145
|
+
options: :options,
|
|
146
|
+
post: :post
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def method_missing(meth, *args, &block)
|
|
150
|
+
if Verbs.keys.include?(meth.to_sym)
|
|
151
|
+
pattern = args.shift
|
|
152
|
+
define_route(meth, pattern, *args, &block)
|
|
153
|
+
else
|
|
154
|
+
super
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def define_route(request_method, route_pattern, *args, &block)
|
|
159
|
+
request_method = Verbs.fetch(request_method.to_sym, :get)
|
|
160
|
+
bucket = table[request_method] ||= {}
|
|
161
|
+
options = args.extract_options!
|
|
162
|
+
|
|
163
|
+
name = options.fetch(:as, "#{ request_method }_#{ counter }")
|
|
164
|
+
|
|
165
|
+
describe_route(request_method, route_pattern)
|
|
166
|
+
|
|
167
|
+
rules << bucket[route_pattern] = [
|
|
168
|
+
options.merge(name: name, method: request_method, args: args, pattern: route_pattern, template: Smooth.util.uri_template(route_pattern)),
|
|
169
|
+
block
|
|
170
|
+
]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def describe_route(request_method, route_pattern)
|
|
174
|
+
documentation = descriptions[request_method] ||= {}
|
|
175
|
+
|
|
176
|
+
if description = descriptions[:current]
|
|
177
|
+
documentation[route_pattern] = description
|
|
178
|
+
descriptions.delete(:current)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Allows for a configuration syntax like
|
|
183
|
+
#
|
|
184
|
+
# routes do
|
|
185
|
+
# get "/books", :to => :query
|
|
186
|
+
# end
|
|
187
|
+
#
|
|
188
|
+
# the lookup_handler_for method will attempt to
|
|
189
|
+
# discern which object is best suited to handle the
|
|
190
|
+
# request based on the http verb and the signifier
|
|
191
|
+
def lookup_handler_for(method, signifier)
|
|
192
|
+
method = method.to_sym
|
|
193
|
+
signifier = signifier.to_sym
|
|
194
|
+
|
|
195
|
+
resource = self.resource
|
|
196
|
+
|
|
197
|
+
case
|
|
198
|
+
|
|
199
|
+
when method == :get && signifier == :query
|
|
200
|
+
->(req) { resource.fetch(:query, :default).respond_to_request(req) }
|
|
201
|
+
|
|
202
|
+
when (method == :show || method == :get) && signifier == :show
|
|
203
|
+
->(req) { resource.fetch(:query, :default).respond_to_find_request(req) }
|
|
204
|
+
|
|
205
|
+
when method == :get
|
|
206
|
+
->(req) { resource.fetch(:query, signifier).respond_to_request(req) }
|
|
207
|
+
|
|
208
|
+
# Mutation Methods
|
|
209
|
+
when method == :put || method == :post || method == :delete
|
|
210
|
+
->(req) { resource.fetch(:command, signifier).respond_to_request(req) }
|
|
211
|
+
else
|
|
212
|
+
->(req) { Smooth::ErrorResponse.new('Unable to find matching route', req) }
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Smooth
|
|
2
|
+
class Resource
|
|
3
|
+
module Templating
|
|
4
|
+
FakerGroups = %w(
|
|
5
|
+
Address Lorem Color Company Food HipterIpsum Internet Job Name Movie PhoneNumber Product Unit Vehicle Venue Skill
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
def self.fakers
|
|
9
|
+
FakerGroups.flat_map do |group|
|
|
10
|
+
prefix = group.to_s.underscore.downcase
|
|
11
|
+
space = Faker.const_get(group.to_sym) rescue nil
|
|
12
|
+
|
|
13
|
+
if space && space.class == Module
|
|
14
|
+
(space.methods - Object.methods - [:k, :underscore]).map { |m| "#{prefix}.#{m}" }
|
|
15
|
+
end
|
|
16
|
+
end.compact.uniq
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_from_template(name = nil, *args, &block)
|
|
20
|
+
if name.is_a?(Hash)
|
|
21
|
+
args.unshift(name)
|
|
22
|
+
name = @template_name
|
|
23
|
+
end
|
|
24
|
+
FactoryGirl.create(name || @template_name, *args, &block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def build_from_template(name = nil, *args, &block)
|
|
28
|
+
if name.is_a?(Hash)
|
|
29
|
+
args.unshift(name)
|
|
30
|
+
name = @template_name
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
FactoryGirl.build(name || @template_name, *args, &block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def template(name = nil, *args, &block)
|
|
37
|
+
options = args.extract_options!
|
|
38
|
+
|
|
39
|
+
if name.nil?
|
|
40
|
+
name = model_class.table_name.singularize.to_sym
|
|
41
|
+
@template_name ||= name
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
options[:class] ||= model_class
|
|
45
|
+
|
|
46
|
+
FactoryGirl.define do
|
|
47
|
+
factory(name.to_sym, options, &block)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def template_registered?(name = nil)
|
|
52
|
+
name ||= model_class.table_name.singularize.to_sym
|
|
53
|
+
!!(FactoryGirl.factory_by_name(name) rescue nil)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Just allows us to wrap template definitions
|
|
57
|
+
def templates(&block)
|
|
58
|
+
instance_eval(&block)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Smooth
|
|
2
|
+
class Response
|
|
3
|
+
attr_reader :outcome, :serializer_options
|
|
4
|
+
|
|
5
|
+
attr_accessor :request_headers, :serializer, :event_namespace, :command_action, :success, :object, :serializer_klass, :current_user
|
|
6
|
+
|
|
7
|
+
def initialize(outcome, serializer_options = {})
|
|
8
|
+
@outcome = outcome
|
|
9
|
+
@serializer_options = serializer_options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_rack
|
|
13
|
+
[status, headers, [body]]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def headers
|
|
17
|
+
{
|
|
18
|
+
'Content-Type' => 'application/json'
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def options
|
|
23
|
+
if success? && serializer
|
|
24
|
+
(@serializer_options || {}).merge(serializer: serializer, scope: current_user)
|
|
25
|
+
else
|
|
26
|
+
@serializer_options.merge(scope: current_user)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def body
|
|
31
|
+
serializer.new(object, options).to_json(options)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def object
|
|
35
|
+
@object || begin
|
|
36
|
+
if success?
|
|
37
|
+
outcome.result
|
|
38
|
+
else
|
|
39
|
+
outcome.errors.message
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def success?
|
|
45
|
+
@success || (outcome && outcome.success?)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def status
|
|
49
|
+
case
|
|
50
|
+
when success?
|
|
51
|
+
200
|
|
52
|
+
else
|
|
53
|
+
400
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class ErrorResponse < Response
|
|
59
|
+
def initialize(error_message, _request_object, *_args)
|
|
60
|
+
@error_message = error_message
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def success?
|
|
64
|
+
false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def body
|
|
68
|
+
{
|
|
69
|
+
error: error_message
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/smooth/serializer.rb
CHANGED
|
@@ -3,12 +3,50 @@ module Smooth
|
|
|
3
3
|
include Smooth::Documentation
|
|
4
4
|
|
|
5
5
|
class_attribute :attribute_descriptions,
|
|
6
|
-
:relationship_descriptions
|
|
6
|
+
:relationship_descriptions,
|
|
7
|
+
:resource_route_variables
|
|
7
8
|
|
|
8
9
|
self.attribute_descriptions = {}.to_mash
|
|
9
10
|
self.relationship_descriptions = {}.to_mash
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
# WIP
|
|
13
|
+
# Need to determine how to access the serialized version
|
|
14
|
+
# as it exists in the context of the serializer instance
|
|
15
|
+
def expand_routes(*slice)
|
|
16
|
+
expanded = parent_resource.expand_routes(route_variables)
|
|
17
|
+
|
|
18
|
+
unless slice.empty?
|
|
19
|
+
expanded = expanded.send(:slice, *slice)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
expanded.transform_keys do |key|
|
|
23
|
+
"#{ key }_url"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def route_variables
|
|
28
|
+
serializer = self
|
|
29
|
+
|
|
30
|
+
self.class.route_variables.reduce({}) do |memo, var|
|
|
31
|
+
value = case
|
|
32
|
+
when serializer.respond_to?(var)
|
|
33
|
+
serializer.send(var)
|
|
34
|
+
when serializer.object.respond_to?(var)
|
|
35
|
+
serializer.object.send(var)
|
|
36
|
+
else
|
|
37
|
+
serializer.read_attribute_for_serialization(var)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
memo[var] = value
|
|
41
|
+
memo
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.route_variables
|
|
46
|
+
@resource_route_variables ||= parent_resource.router.route_patterns_table.map { |p| _, h = p; h[:variables] }.flatten.compact.uniq
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.method_added(method_name)
|
|
12
50
|
if documented = inline_description
|
|
13
51
|
attribute_descriptions[method_name.to_sym] = documented
|
|
14
52
|
end
|
|
@@ -22,7 +60,7 @@ module Smooth
|
|
|
22
60
|
schema[:associations]
|
|
23
61
|
end
|
|
24
62
|
|
|
25
|
-
def self.configure
|
|
63
|
+
def self.configure(options, resource = nil)
|
|
26
64
|
resource ||= Smooth.current_resource
|
|
27
65
|
klass = define_or_open(options, resource)
|
|
28
66
|
|
|
@@ -38,22 +76,53 @@ module Smooth
|
|
|
38
76
|
base = Smooth.serializer
|
|
39
77
|
|
|
40
78
|
name = options.name
|
|
41
|
-
name = nil if name ==
|
|
79
|
+
name = nil if name == 'Default'
|
|
80
|
+
|
|
81
|
+
klass = "#{ resource.model_class }#{ name }".singularize + 'Serializer'
|
|
42
82
|
|
|
43
|
-
klass =
|
|
83
|
+
klass = klass.gsub(/\s+/, '')
|
|
44
84
|
|
|
45
85
|
if serializer_klass = Object.const_get(klass) rescue nil
|
|
46
86
|
return serializer_klass
|
|
47
87
|
end
|
|
48
88
|
|
|
49
|
-
|
|
89
|
+
parent_klass = Class.new(base)
|
|
90
|
+
|
|
91
|
+
parent_klass.belongs_to_resource(resource)
|
|
92
|
+
|
|
93
|
+
begin
|
|
94
|
+
Object.const_set(klass, parent_klass)
|
|
95
|
+
rescue => ex
|
|
96
|
+
puts ex.message
|
|
97
|
+
puts "Error setting #{ klass } #{ base }. klass is a #{ klass.class }"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
parent_klass
|
|
50
101
|
end
|
|
51
102
|
|
|
52
|
-
|
|
103
|
+
class_attribute :parent_resource
|
|
104
|
+
|
|
105
|
+
def parent_resource
|
|
106
|
+
self.class.parent_resource
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def parent_api
|
|
110
|
+
self.class.parent_api
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.belongs_to_resource(resource)
|
|
114
|
+
self.parent_resource = resource
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.parent_api
|
|
118
|
+
parent_resource.api
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.documentation_for_attribute(attribute)
|
|
53
122
|
attribute_descriptions[attribute.to_sym]
|
|
54
123
|
end
|
|
55
124
|
|
|
56
|
-
def self.documentation_for_association
|
|
125
|
+
def self.documentation_for_association(association)
|
|
57
126
|
relationship_descriptions[association.to_sym]
|
|
58
127
|
end
|
|
59
128
|
|
|
@@ -61,7 +130,11 @@ module Smooth
|
|
|
61
130
|
attribute_descriptions.merge(relationship_descriptions).to_mash
|
|
62
131
|
end
|
|
63
132
|
|
|
64
|
-
def self.
|
|
133
|
+
def self.interface_documentation
|
|
134
|
+
documentation
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.attribute(attr, options = {})
|
|
65
138
|
documented = inline_description
|
|
66
139
|
|
|
67
140
|
if documented
|
|
@@ -71,7 +144,13 @@ module Smooth
|
|
|
71
144
|
super
|
|
72
145
|
end
|
|
73
146
|
|
|
74
|
-
def self.
|
|
147
|
+
def self.computed(*args, &block)
|
|
148
|
+
property_name = args.first
|
|
149
|
+
send(:define_method, property_name, &block)
|
|
150
|
+
send(:attribute, *args)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.has_one(attr, options = {})
|
|
75
154
|
documented = inline_description
|
|
76
155
|
|
|
77
156
|
if documented
|
|
@@ -81,7 +160,7 @@ module Smooth
|
|
|
81
160
|
super
|
|
82
161
|
end
|
|
83
162
|
|
|
84
|
-
def self.has_many
|
|
163
|
+
def self.has_many(attr, options = {})
|
|
85
164
|
documented = inline_description
|
|
86
165
|
|
|
87
166
|
if documented
|
|
@@ -90,5 +169,17 @@ module Smooth
|
|
|
90
169
|
|
|
91
170
|
super
|
|
92
171
|
end
|
|
172
|
+
|
|
173
|
+
def self.return_ids_for_relationships!
|
|
174
|
+
@returns_ids_for_relationships = true
|
|
175
|
+
embed :ids
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def self.returns_ids_for_relationships?
|
|
179
|
+
@returns_ids_for_relationships == true
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
class ArraySerializer < ActiveModel::ArraySerializer
|
|
93
184
|
end
|
|
94
185
|
end
|