shaf 2.1.0 → 3.0.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/shaf/app.rb +27 -9
- data/lib/shaf/command/base.rb +4 -0
- data/lib/shaf/command/new.rb +5 -1
- data/lib/shaf/command/server.rb +5 -1
- data/lib/shaf/command/templates/config/settings.yml.erb +9 -8
- data/lib/shaf/extensions/resource_uris.rb +37 -155
- data/lib/shaf/extensions/symbolic_routes.rb +5 -18
- data/lib/shaf/formable/builder.rb +58 -19
- data/lib/shaf/formable/form.rb +13 -10
- data/lib/shaf/formable.rb +10 -23
- data/lib/shaf/generator/base.rb +82 -0
- data/lib/shaf/generator/controller.rb +19 -35
- data/lib/shaf/generator/forms.rb +10 -14
- data/lib/shaf/generator/migration/add_column.rb +0 -4
- data/lib/shaf/generator/migration/add_index.rb +0 -4
- data/lib/shaf/generator/migration/base.rb +8 -0
- data/lib/shaf/generator/migration/create_table.rb +0 -4
- data/lib/shaf/generator/migration/drop_column.rb +0 -4
- data/lib/shaf/generator/migration/rename_column.rb +0 -4
- data/lib/shaf/generator/model.rb +29 -14
- data/lib/shaf/generator/policy.rb +8 -14
- data/lib/shaf/generator/profile.rb +9 -14
- data/lib/shaf/generator/scaffold.rb +6 -9
- data/lib/shaf/generator/serializer.rb +31 -30
- data/lib/shaf/generator/templates/api/controller.rb.erb +13 -13
- data/lib/shaf/generator/templates/api/forms.rb.erb +2 -2
- data/lib/shaf/generator/templates/api/model.rb.erb +1 -1
- data/lib/shaf/generator/templates/api/profile.rb.erb +1 -1
- data/lib/shaf/generator/templates/api/serializer.rb.erb +1 -1
- data/lib/shaf/generator/templates/spec/integration_spec.rb.erb +14 -14
- data/lib/shaf/helpers/paginate.rb +1 -1
- data/lib/shaf/link_relations.rb +77 -0
- data/lib/shaf/profile.rb +8 -8
- data/lib/shaf/registrable_factory.rb +62 -32
- data/lib/shaf/responder/problem_json.rb +1 -1
- data/lib/shaf/router.rb +65 -12
- data/lib/shaf/spec/integration_spec.rb +1 -1
- data/lib/shaf/tasks.rb +0 -1
- data/lib/shaf/upgrade/package.rb +5 -7
- data/lib/shaf/utils.rb +2 -2
- data/lib/shaf/version.rb +1 -1
- data/lib/shaf/yard/link_object.rb +2 -3
- data/templates/Rakefile +0 -6
- data/templates/api/controllers/base_controller.rb +0 -2
- data/templates/api/serializers/root_serializer.rb +0 -11
- data/templates/config/initializers/middleware.rb +6 -0
- data/templates/spec/spec_helper.rb +4 -1
- data/upgrades/3.0.0.tar.gz +0 -0
- data/yard_templates/api_doc/profile_attribute/html/attribute.erb +2 -1
- data/yard_templates/api_doc/resource_attribute/html/attribute.erb +2 -1
- data/yard_templates/api_doc/sidebar/html/profile_list.erb +2 -1
- data/yard_templates/api_doc/sidebar/html/serializer_list.erb +2 -1
- data.tar.gz.sig +0 -0
- metadata +34 -36
- metadata.gz.sig +0 -0
- data/lib/shaf/api_doc/comment.rb +0 -27
- data/lib/shaf/api_doc/document.rb +0 -137
- data/lib/shaf/api_doc/link_relations.rb +0 -77
- data/lib/shaf/middleware.rb +0 -1
- data/lib/shaf/tasks/api_doc_task.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c508aa7154d738ed2009c18256326f590917af8c24d60bfacfd5bb91613c4270
|
4
|
+
data.tar.gz: 49e9fec1c522764bb1a722623cfb49cf8de3d6d7e026355a09310a6099828711
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1d2ddda76cb3e76470f4cbc0297e72ef3c3b9c6119a12d101d999a50bc0aab37a442067ddd0cb4fdd3e698d0446982e65d199e633e46d875175e087f3bc9a61
|
7
|
+
data.tar.gz: 3d2c97ed1362ac58addf9c282e37d8754adad5a994c421d72779e4576dd91c8cbd1bbfc67689d20bf51bc20410270f61752383d36b11eb9dcbe28883c6552440
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/shaf/app.rb
CHANGED
@@ -1,25 +1,43 @@
|
|
1
|
-
require '
|
1
|
+
require 'sinatra'
|
2
2
|
|
3
3
|
module Shaf
|
4
4
|
class App
|
5
5
|
class << self
|
6
6
|
# Either call `Shaf::App.run!`
|
7
7
|
def run!
|
8
|
-
|
8
|
+
instance.run!
|
9
9
|
end
|
10
10
|
|
11
11
|
# Or `run Shaf::App` (in config.ru)
|
12
12
|
def call(*args)
|
13
|
-
|
13
|
+
instance.call(*args)
|
14
14
|
end
|
15
15
|
|
16
|
+
def instance
|
17
|
+
# This works since Sinatra includes Sinatra::Delegator into
|
18
|
+
# Rack::Builder, which means that Rack::Builder#set will be delegated
|
19
|
+
# to Sinatra::Application
|
20
|
+
@instance ||= Rack::Builder.new(app) do
|
21
|
+
set :port, Settings.port || 3000
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def use(middleware, *args, **kwargs, &block)
|
26
|
+
if args.empty? && kwargs.empty?
|
27
|
+
instance.use middleware, &block
|
28
|
+
elsif kwargs.empty?
|
29
|
+
instance.use middleware, *args, &block
|
30
|
+
elsif args.empty?
|
31
|
+
instance.use middleware, **kwargs, &block
|
32
|
+
else
|
33
|
+
instance.use middleware, *args, **kwargs, &block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
16
39
|
def app
|
17
|
-
|
18
|
-
Sinatra.new.tap do |app|
|
19
|
-
app.set :port, Settings.port || 3000
|
20
|
-
app.use Middleware::RequestId
|
21
|
-
app.use Router
|
22
|
-
end
|
40
|
+
Router.new
|
23
41
|
end
|
24
42
|
end
|
25
43
|
end
|
data/lib/shaf/command/base.rb
CHANGED
data/lib/shaf/command/new.rb
CHANGED
@@ -47,8 +47,12 @@ module Shaf
|
|
47
47
|
settings_file = 'config/settings.yml'
|
48
48
|
template_file = File.expand_path("../templates/#{settings_file}.erb", __FILE__)
|
49
49
|
content = File.read(template_file)
|
50
|
+
locals = {
|
51
|
+
project_name: project_name.capitalize,
|
52
|
+
default_port: "<%= ENV.fetch('PORT', 3000) %>"
|
53
|
+
}
|
50
54
|
File.write settings_file,
|
51
|
-
erb(content,
|
55
|
+
erb(content, locals)
|
52
56
|
end
|
53
57
|
|
54
58
|
def erb(content, locals = {})
|
data/lib/shaf/command/server.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'rack'
|
2
3
|
|
3
4
|
module Shaf
|
4
5
|
module Command
|
@@ -16,7 +17,10 @@ module Shaf
|
|
16
17
|
def call
|
17
18
|
Settings.port = options[:port] if options[:port]
|
18
19
|
bootstrap
|
19
|
-
|
20
|
+
Rack::Server.start(
|
21
|
+
app: App,
|
22
|
+
Port: Settings.port
|
23
|
+
)
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -1,23 +1,24 @@
|
|
1
1
|
---
|
2
2
|
default: &default
|
3
|
-
|
4
|
-
public_folder: frontend/assets
|
5
|
-
views_folder: frontend/views
|
3
|
+
default_authentication_realm: api
|
6
4
|
documents_dir: doc/api
|
7
|
-
migrations_dir: db/migrations
|
8
5
|
fixtures_dir: spec/fixtures
|
9
|
-
|
6
|
+
hostname: localhost
|
10
7
|
http_cache: on
|
11
8
|
http_cache_max_age_long: 86400 # 60 * 60 * 24 = 1 day
|
12
9
|
http_cache_max_age_short: 3600 # 60 * 60 = 1 hour
|
13
|
-
|
10
|
+
migrations_dir: db/migrations
|
11
|
+
paginate_per_page: 25
|
12
|
+
port: <%= default_port %>
|
13
|
+
project_name: <%= project_name %>
|
14
14
|
protocol: http
|
15
|
-
|
15
|
+
public_folder: frontend/assets
|
16
|
+
views_folder: frontend/views
|
16
17
|
|
17
18
|
production:
|
18
19
|
<<: *default
|
19
|
-
port: <%= ENV.fetch('PORT', 443) %>
|
20
20
|
base_uri: https://my.public.shaf.api.com
|
21
|
+
port: <%= ENV.fetch('PORT', 443) %>
|
21
22
|
|
22
23
|
development:
|
23
24
|
<<: *default
|
@@ -106,26 +106,20 @@ module Shaf
|
|
106
106
|
# edit_book_uri_template => /books/:id/edit
|
107
107
|
#
|
108
108
|
class CreateUriMethods
|
109
|
-
def initialize(name, base: nil,
|
109
|
+
def initialize(name, plural_name: nil, base: nil, namespace: nil, only: nil, except: nil)
|
110
110
|
@name = name.to_s
|
111
|
-
@base = base&.sub(%r(/\Z), '') || ''
|
112
111
|
@plural_name = plural_name&.to_s || Utils::pluralize(name.to_s)
|
112
|
+
@base = base&.sub(%r(/\Z), '') || ''
|
113
|
+
@base = "/#{namespace}" if namespace && !base
|
114
|
+
@namespace = namespace
|
113
115
|
@only = only
|
114
116
|
@except = except
|
115
117
|
@added_path_methods = []
|
116
118
|
end
|
117
119
|
|
118
120
|
def call
|
119
|
-
|
120
|
-
|
121
|
-
# Remove this branch and only keep the `else` behavior when dropping
|
122
|
-
# support for this
|
123
|
-
register_resource_helper_by_arg
|
124
|
-
else
|
125
|
-
register_collection_helper
|
126
|
-
register_resource_helper
|
127
|
-
end
|
128
|
-
|
121
|
+
register_collection_helper
|
122
|
+
register_resource_helper
|
129
123
|
register_new_resource_helper
|
130
124
|
register_edit_resource_helper
|
131
125
|
@added_path_methods
|
@@ -133,51 +127,38 @@ module Shaf
|
|
133
127
|
|
134
128
|
private
|
135
129
|
|
136
|
-
attr_reader :name, :base, :
|
130
|
+
attr_reader :name, :plural_name, :base, :namespace, :only, :except
|
137
131
|
|
138
132
|
def register_collection_helper
|
139
133
|
return if skip? :collection
|
140
134
|
|
135
|
+
method = method_name(plural_name, name == plural_name)
|
141
136
|
template_uri = "#{base}/#{plural_name}".freeze
|
142
|
-
|
143
|
-
method_name = "#{name}_collection" if name == @plural_name
|
144
|
-
register(method_name, template_uri)
|
137
|
+
register(method, template_uri)
|
145
138
|
end
|
146
139
|
|
147
140
|
def register_resource_helper
|
148
141
|
return if skip? :resource
|
149
142
|
|
143
|
+
method = method_name(name)
|
150
144
|
template_uri = "#{base}/#{plural_name}/:id".freeze
|
151
|
-
register(
|
152
|
-
end
|
153
|
-
|
154
|
-
# If a resource has the same singular and plural names, then this method
|
155
|
-
# should be used. It will return the resource uri when a resource is given
|
156
|
-
# as argument and the resources uri when no arguments are provided.
|
157
|
-
def register_resource_helper_by_arg
|
158
|
-
return register_resource_helper if skip? :collection
|
159
|
-
register_collection_helper
|
160
|
-
return if skip? :new
|
161
|
-
|
162
|
-
resource_template_uri = "#{base}/#{plural_name}/:id"
|
163
|
-
collection_template_uri = "#{base}/#{plural_name}"
|
164
|
-
|
165
|
-
builder = MethodBuilder.new(name, resource_template_uri, alt_uri: collection_template_uri)
|
166
|
-
@added_path_methods << builder.call
|
145
|
+
register(method, template_uri)
|
167
146
|
end
|
168
147
|
|
169
148
|
def register_new_resource_helper
|
170
149
|
return if skip? :new
|
171
150
|
|
151
|
+
method = method_name(name)
|
172
152
|
template_uri = "#{base}/#{name}/form".freeze
|
173
|
-
register("new_#{
|
153
|
+
register("new_#{method}", template_uri)
|
174
154
|
end
|
175
155
|
|
176
156
|
def register_edit_resource_helper
|
177
157
|
return if skip? :edit
|
178
158
|
|
159
|
+
method = method_name(name)
|
179
160
|
template_uri = "#{base}/#{plural_name}/:id/edit".freeze
|
180
|
-
register("edit_#{
|
161
|
+
register("edit_#{method}", template_uri)
|
181
162
|
end
|
182
163
|
|
183
164
|
def register(name, template_uri)
|
@@ -194,11 +175,14 @@ module Shaf
|
|
194
175
|
false
|
195
176
|
end
|
196
177
|
end
|
178
|
+
|
179
|
+
def method_name(name, collection = false)
|
180
|
+
collection_str = "collection" if collection
|
181
|
+
[namespace, name, collection_str].compact.join('_')
|
182
|
+
end
|
197
183
|
end
|
198
184
|
|
199
185
|
class MethodBuilder
|
200
|
-
NO_GIVEN_VALUE = Object.new
|
201
|
-
|
202
186
|
def self.query_string(query)
|
203
187
|
return '' unless query&.any?
|
204
188
|
|
@@ -211,10 +195,9 @@ module Shaf
|
|
211
195
|
[query_str, fragment_str].join
|
212
196
|
end
|
213
197
|
|
214
|
-
def initialize(name, uri
|
198
|
+
def initialize(name, uri)
|
215
199
|
@name = name
|
216
200
|
@uri = uri.dup.freeze
|
217
|
-
@alt_uri = alt_uri.dup.freeze
|
218
201
|
end
|
219
202
|
|
220
203
|
def call
|
@@ -223,16 +206,12 @@ module Shaf
|
|
223
206
|
raise exception.new(name, uri_method_name)
|
224
207
|
end
|
225
208
|
|
226
|
-
|
227
|
-
build_methods
|
228
|
-
else
|
229
|
-
build_methods_with_optional_arg
|
230
|
-
end
|
209
|
+
build_methods
|
231
210
|
end
|
232
211
|
|
233
212
|
private
|
234
213
|
|
235
|
-
attr_reader :name, :uri
|
214
|
+
attr_reader :name, :uri
|
236
215
|
|
237
216
|
def build_methods
|
238
217
|
UriHelperMethods.eval_method uri_method_string
|
@@ -243,15 +222,6 @@ module Shaf
|
|
243
222
|
path_method_name.to_sym
|
244
223
|
end
|
245
224
|
|
246
|
-
def build_methods_with_optional_arg
|
247
|
-
UriHelperMethods.eval_method uri_method_with_optional_arg_string
|
248
|
-
UriHelperMethods.eval_method path_method_with_optional_arg_string
|
249
|
-
UriHelperMethods.register(template_method_name, &template_proc)
|
250
|
-
UriHelperMethods.register(legacy_template_method_name, &template_proc)
|
251
|
-
UriHelperMethods.register(path_matcher_name, &path_matcher_proc)
|
252
|
-
path_method_name.to_sym
|
253
|
-
end
|
254
|
-
|
255
225
|
def uri_method_name
|
256
226
|
"#{name}_uri".freeze
|
257
227
|
end
|
@@ -309,53 +279,6 @@ module Shaf
|
|
309
279
|
RUBY
|
310
280
|
end
|
311
281
|
|
312
|
-
def uri_method_with_optional_arg_string
|
313
|
-
base_uri = UriHelper.base_uri
|
314
|
-
arg_no = extract_symbols(alt_uri).size
|
315
|
-
<<~RUBY
|
316
|
-
def #{uri_signature(uri: alt_uri, optional_args: 1)}
|
317
|
-
query_str = Shaf::MethodBuilder.query_string(query)
|
318
|
-
if arg#{arg_no}.nil?
|
319
|
-
warn <<~DEPRECATION
|
320
|
-
|
321
|
-
Deprecated use of collection uri helper:
|
322
|
-
To get the collection uri use ##{name}_collection_uri instead of ##{uri_method_name}.
|
323
|
-
Or pass an argument to ##{uri_method_name} to get the uri to a resource.
|
324
|
-
\#{caller.find { |s| !s.match? %r{lib/shaf/extensions/resource_uris.rb} }}
|
325
|
-
|
326
|
-
DEPRECATION
|
327
|
-
|
328
|
-
\"#{base_uri}#{interpolated_uri_string(alt_uri)}\#{query_str}\".freeze
|
329
|
-
else
|
330
|
-
\"#{base_uri}#{interpolated_uri_string(uri)}\#{query_str}\".freeze
|
331
|
-
end
|
332
|
-
end
|
333
|
-
RUBY
|
334
|
-
end
|
335
|
-
|
336
|
-
def path_method_with_optional_arg_string
|
337
|
-
arg_no = extract_symbols(alt_uri).size
|
338
|
-
<<~RUBY
|
339
|
-
def #{path_signature(uri: alt_uri, optional_args: 1)}
|
340
|
-
query_str = Shaf::MethodBuilder.query_string(query)
|
341
|
-
if arg#{arg_no}.nil?
|
342
|
-
warn <<~DEPRECATION
|
343
|
-
|
344
|
-
Deprecated use of collection path helper:
|
345
|
-
To get the collection path use ##{name}_collection_path instead of ##{path_method_name}.
|
346
|
-
Or pass an argument to ##{path_method_name} to get the path to a resource.
|
347
|
-
\#{caller.find { |s| !s.match? %r{lib/shaf/extensions} }}
|
348
|
-
|
349
|
-
DEPRECATION
|
350
|
-
|
351
|
-
\"#{interpolated_uri_string(alt_uri)}\#{query_str}\".freeze
|
352
|
-
else
|
353
|
-
\"#{interpolated_uri_string(uri)}\#{query_str}\".freeze
|
354
|
-
end
|
355
|
-
end
|
356
|
-
RUBY
|
357
|
-
end
|
358
|
-
|
359
282
|
def extract_symbols(uri = @uri)
|
360
283
|
uri.split('/').grep(/\A:.+/).map { |t| t[1..-1].to_sym }
|
361
284
|
end
|
@@ -384,70 +307,29 @@ module Shaf
|
|
384
307
|
end
|
385
308
|
|
386
309
|
def template_proc
|
387
|
-
uri
|
388
|
-
|
389
|
-
if alt_uri.nil?
|
390
|
-
-> { uri }
|
391
|
-
else
|
392
|
-
deprecated_method = template_method_name
|
393
|
-
replacing_method = "#{name}_collection_path_template"
|
394
|
-
|
395
|
-
lambda do |collection = NO_GIVEN_VALUE|
|
396
|
-
if collection != NO_GIVEN_VALUE
|
397
|
-
warn <<~DEPRECATION
|
398
|
-
|
399
|
-
Deprecated use of uri template helper with `collection` argument:
|
400
|
-
Use #{replacing_method} instead of #{deprecated_method}"
|
401
|
-
#{caller.find { |s| !s.match? %r{lib/shaf/extensions} }}
|
402
|
-
|
403
|
-
DEPRECATION
|
404
|
-
else
|
405
|
-
collection = false
|
406
|
-
end
|
407
|
-
|
408
|
-
collection ? alt_uri : uri
|
409
|
-
end
|
410
|
-
end
|
310
|
+
uri = @uri
|
311
|
+
-> { uri }
|
411
312
|
end
|
412
313
|
|
413
|
-
def
|
414
|
-
[
|
415
|
-
|
416
|
-
alt_uri&.gsub(%r{:[^/]*}, '\w+')
|
417
|
-
].compact.map { |str| Regexp.new("\\A#{str}\\Z") }
|
314
|
+
def path_mather_pattern
|
315
|
+
pattern = uri.gsub(%r{:[^/]*}, '\w+')
|
316
|
+
Regexp.new("\\A#{pattern}\\Z")
|
418
317
|
end
|
419
318
|
|
420
319
|
def path_matcher_proc
|
421
|
-
|
422
|
-
|
423
|
-
deprecated_method = path_matcher_name
|
424
|
-
replacing_method = "#{name}_collection_path?"
|
425
|
-
|
426
|
-
lambda do |path = nil, collection: NO_GIVEN_VALUE|
|
427
|
-
if collection != NO_GIVEN_VALUE
|
428
|
-
warn <<~DEPRECATION
|
429
|
-
|
430
|
-
Deprecated use of uri predicate helper with `collection` argument:
|
431
|
-
Use #{replacing_method} instead of #{deprecated_method}(collection: true)
|
432
|
-
#{caller.find { |s| !s.match? %r{lib/shaf/extensions} }}
|
433
|
-
|
434
|
-
DEPRECATION
|
435
|
-
else
|
436
|
-
collection = false
|
437
|
-
end
|
320
|
+
pattern = path_mather_pattern
|
438
321
|
|
322
|
+
lambda do |path = nil|
|
439
323
|
unless path
|
440
324
|
r = request if respond_to? :request
|
441
|
-
path = r.path_info if r
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
)
|
448
|
-
end
|
325
|
+
path = r.path_info if r.respond_to? :path_info
|
326
|
+
|
327
|
+
raise(
|
328
|
+
ArgumentError,
|
329
|
+
"Uri must be given (or #{self} should respond to :request)"
|
330
|
+
) unless path
|
449
331
|
end
|
450
|
-
|
332
|
+
|
451
333
|
!!(pattern =~ path)
|
452
334
|
end
|
453
335
|
end
|
@@ -4,30 +4,17 @@ module Shaf
|
|
4
4
|
|
5
5
|
Shaf::SUPPORTED_HTTP_METHODS.each do |m|
|
6
6
|
define_method m do |path, **options, &block|
|
7
|
-
|
8
|
-
path = rewrite_path(path, collection, m)
|
7
|
+
path = rewrite_path(path, m)
|
9
8
|
super(path, **options, &block)
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
13
|
-
def rewrite_path(path,
|
12
|
+
def rewrite_path(path, method)
|
14
13
|
return path unless path.is_a? Symbol
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
instead of `#{method} :#{path}, collection: #{collection} do`
|
20
|
-
#{caller.find { |s| s.match?(/_controller.rb/) }}
|
21
|
-
|
22
|
-
DEPRECATION
|
23
|
-
|
24
|
-
method = "#{path}_template"
|
25
|
-
send_args = [method]
|
26
|
-
send_args << collection unless collection.nil?
|
27
|
-
return send(*send_args) if respond_to? method
|
28
|
-
|
29
|
-
method = "#{path}_path_template"
|
30
|
-
return send(*send_args) if respond_to? method
|
15
|
+
["#{path}_template", "#{path}_path_template"].each do |method|
|
16
|
+
return send(method) if respond_to? method
|
17
|
+
end
|
31
18
|
|
32
19
|
raise UriHelperNotRegisterdError, <<~RUBY
|
33
20
|
Undefined method '#{method}'. Did you forget to register a uri helper for #{path}?
|
@@ -1,11 +1,56 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'shaf/formable/form'
|
3
|
+
require 'shaf/immutable_attr'
|
2
4
|
|
3
5
|
module Shaf
|
4
6
|
module Formable
|
5
7
|
class Builder
|
6
|
-
InstanceAccessorType = Struct.new(:prefill?)
|
7
8
|
DELEGATES = %i[title name action method type submit fields].freeze
|
8
9
|
|
10
|
+
class FormWrapper
|
11
|
+
extend Forwardable
|
12
|
+
extend Shaf::ImmutableAttr
|
13
|
+
|
14
|
+
attr_accessor :instance_accessor
|
15
|
+
immutable_reader :form
|
16
|
+
|
17
|
+
WRITER_DELEGATES = DELEGATES.map { |method| :"#{method}=" }
|
18
|
+
def_delegators :@form, :add_field, *WRITER_DELEGATES
|
19
|
+
|
20
|
+
def initialize(form, method_name: nil, instance_accessor: nil)
|
21
|
+
@form = form&.dup || Formable::Form.new
|
22
|
+
@method_name = method_name
|
23
|
+
@form.action = action_from(method_name) unless @form.action
|
24
|
+
@instance_accessor = instance_accessor
|
25
|
+
end
|
26
|
+
|
27
|
+
def action_from(method_name)
|
28
|
+
return unless method_name
|
29
|
+
|
30
|
+
(method_name.to_s.delete_suffix('_form')).to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_name
|
34
|
+
name = @method_name || form.action
|
35
|
+
|
36
|
+
if name.nil?
|
37
|
+
nil
|
38
|
+
elsif name.to_s.end_with? '_form'
|
39
|
+
name.to_sym
|
40
|
+
else
|
41
|
+
:"#{name}_form"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def instance_accessor?
|
46
|
+
[:empty, :prefill].include? instance_accessor
|
47
|
+
end
|
48
|
+
|
49
|
+
def prefill?
|
50
|
+
instance_accessor == :prefill
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
9
54
|
attr_reader :forms
|
10
55
|
|
11
56
|
def initialize(&block)
|
@@ -15,48 +60,42 @@ module Shaf
|
|
15
60
|
exec_with_form(block)
|
16
61
|
end
|
17
62
|
|
18
|
-
def instance_accessor_for(form)
|
19
|
-
@instance_accessors[form.action]
|
20
|
-
end
|
21
|
-
|
22
63
|
private
|
23
64
|
|
24
|
-
attr_reader :
|
65
|
+
attr_reader :current
|
25
66
|
|
26
|
-
def exec_with_form(block,
|
27
|
-
|
28
|
-
form.action = action if action
|
67
|
+
def exec_with_form(block, method_name: nil)
|
68
|
+
prev, @current = current, new_form(method_name)
|
29
69
|
instance_exec(&block)
|
30
70
|
ensure
|
31
|
-
@
|
71
|
+
@current = prev
|
32
72
|
end
|
33
73
|
|
34
|
-
def new_form
|
35
|
-
(form
|
74
|
+
def new_form(method_name)
|
75
|
+
FormWrapper.new(current&.form, method_name: method_name).tap { |f| @forms << f }
|
36
76
|
end
|
37
77
|
|
38
78
|
def instance_accessor(prefill: true)
|
39
|
-
|
40
|
-
@instance_accessors[form.action] = acc
|
79
|
+
current.instance_accessor = prefill ? :prefill : :empty
|
41
80
|
end
|
42
81
|
|
43
82
|
DELEGATES.each do |name|
|
44
83
|
define_method(name) do |arg|
|
45
|
-
|
84
|
+
current.send(:"#{name}=", arg)
|
46
85
|
end
|
47
86
|
end
|
48
87
|
|
49
88
|
def field(name, opts = {})
|
50
|
-
|
89
|
+
current.add_field(name, opts)
|
51
90
|
end
|
52
91
|
|
53
92
|
def method_missing(method, *args, &block)
|
54
93
|
return super unless args.empty? && block
|
55
|
-
exec_with_form(block,
|
94
|
+
exec_with_form(block, method_name: method)
|
56
95
|
end
|
57
96
|
|
58
|
-
def respond_to_missing?(
|
59
|
-
true
|
97
|
+
def respond_to_missing?(method, _include_private = false)
|
98
|
+
method.to_s.end_with?('_form') ? true : super
|
60
99
|
end
|
61
100
|
end
|
62
101
|
end
|
data/lib/shaf/formable/form.rb
CHANGED
@@ -12,14 +12,15 @@ module Shaf
|
|
12
12
|
DEFAULT_SUBMIT = 'save'.freeze
|
13
13
|
|
14
14
|
attr_accessor :resource
|
15
|
-
|
16
|
-
|
15
|
+
attr_writer :name, :action
|
16
|
+
immutable_accessor :title, :href, :type, :submit, :self_link
|
17
|
+
immutable_reader :fields
|
17
18
|
|
18
19
|
def initialize(params = {})
|
19
20
|
@title = params[:title]
|
20
21
|
@action = params[:action]
|
21
|
-
@name = params[:name]
|
22
|
-
@method = params[:method]
|
22
|
+
@name = params[:name]
|
23
|
+
@method = params[:method]
|
23
24
|
@type = params[:type] || DEFAULT_TYPE
|
24
25
|
@submit = params[:submit] || DEFAULT_SUBMIT
|
25
26
|
@fields = (params[:fields] || {}).map do |name, args|
|
@@ -27,23 +28,25 @@ module Shaf
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
31
|
+
def name
|
32
|
+
(@name || name_from(action))&.to_sym
|
33
|
+
end
|
34
|
+
|
30
35
|
def method=(http_method)
|
31
36
|
@method = http_method.to_s.upcase
|
32
37
|
end
|
33
38
|
|
34
39
|
def method
|
35
|
-
|
36
|
-
|
40
|
+
_method = @method || http_method_from(action)
|
41
|
+
_method.to_s.upcase if _method
|
37
42
|
end
|
38
43
|
|
39
44
|
def fields=(fields)
|
40
45
|
@fields = fields.map { |name, args| Field.new(name, args) }
|
41
46
|
end
|
42
47
|
|
43
|
-
def action
|
44
|
-
@action
|
45
|
-
@name ||= name_from action
|
46
|
-
@method ||= http_method_from action
|
48
|
+
def action
|
49
|
+
@action&.to_sym
|
47
50
|
end
|
48
51
|
|
49
52
|
def add_field(name, opts)
|