autoforme 1.8.0 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +26 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +66 -4
- data/lib/autoforme/action.rb +11 -6
- data/lib/autoforme/frameworks/rails.rb +5 -1
- data/lib/autoforme/frameworks/roda.rb +25 -2
- data/lib/autoforme/frameworks/sinatra.rb +3 -1
- data/lib/autoforme/model.rb +1 -1
- data/lib/autoforme/models/sequel.rb +35 -6
- data/lib/autoforme/version.rb +1 -1
- data/lib/autoforme.rb +7 -8
- data/lib/roda/plugins/autoforme.rb +1 -1
- data/spec/all.rb +1 -1
- data/spec/associations_spec.rb +45 -1
- data/spec/basic_spec.rb +24 -4
- data/spec/mtm_spec.rb +113 -1
- data/spec/rails_spec_helper.rb +33 -3
- data/spec/roda_spec.rb +1 -1
- data/spec/roda_spec_helper.rb +22 -10
- data/spec/sequel_spec_helper.rb +0 -1
- data/spec/sinatra_spec_helper.rb +1 -2
- data/spec/spec_helper.rb +3 -4
- data/spec/unit_spec.rb +1 -1
- metadata +26 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0198a23636933034920c066694a4acd8e58d5529fbf21ada5f542a7de444cc2c'
|
4
|
+
data.tar.gz: 6cf1f310f06018a91573472c7410c1dda1842b7f96593a82deaf9be5f2bdd524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8931ddb513d806df30b8a4fe36fb20583597ac4e9f45e1144c5732cff66d09f92a14967a5ac4c35724ac339bfd23af866b454490ffabe99b327dfaff2e1aa787
|
7
|
+
data.tar.gz: '097239f8074dc20537dec29064f24c2e5fbb521cf3586bb828726afc2b436eed190794d2b276e9b108c60bb6a4c3116a35dc651d54e32af5ff74d34e4a6b586e'
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
=== 1.11.0 (2021-11-30)
|
2
|
+
|
3
|
+
* Require forme 2.0.0 (jeremyevans)
|
4
|
+
|
5
|
+
* Add support for view_options framework option (nmb, jeremyevans) (#42)
|
6
|
+
|
7
|
+
* Drop support for Ruby 1.8 (jeremyevans)
|
8
|
+
|
9
|
+
=== 1.10.0 (2021-08-27)
|
10
|
+
|
11
|
+
* Do not consider read_only many_to_many associations to be editable (jeremyevans)
|
12
|
+
|
13
|
+
* Ignore unique constraint violations when adding associated objects in mtm_update (jeremyevans)
|
14
|
+
|
15
|
+
* Handle search fields that cannot be typecast correctly by returning no results (jeremyevans)
|
16
|
+
|
17
|
+
=== 1.9.1 (2019-07-22)
|
18
|
+
|
19
|
+
* [SECURITY] Escape object display name when displaying association links (adam12)
|
20
|
+
|
21
|
+
=== 1.9.0 (2018-07-18)
|
22
|
+
|
23
|
+
* Add support for using flash string keys in the Roda support, to work with Roda's sessions plugin (jeremyevans)
|
24
|
+
|
25
|
+
* Show correct page title on error pages (jeremyevans)
|
26
|
+
|
1
27
|
=== 1.8.0 (2018-06-11)
|
2
28
|
|
3
29
|
* Add support for Roda route_csrf plugin for request-specific CSRF tokens (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -22,8 +22,7 @@ flexible in terms of configuration.
|
|
22
22
|
Demo Site :: http://autoforme-demo.jeremyevans.net
|
23
23
|
RDoc :: http://autoforme.jeremyevans.net
|
24
24
|
Source :: https://github.com/jeremyevans/autoforme
|
25
|
-
|
26
|
-
Google Group :: https://groups.google.com/forum/#!forum/ruby-forme
|
25
|
+
Discussion Forum :: https://github.com/jeremyevans/autoforme/discussions
|
27
26
|
Bug Tracker :: https://github.com/jeremyevans/autoforme/issues
|
28
27
|
|
29
28
|
= Features
|
@@ -161,12 +160,13 @@ These options are related to displayed output:
|
|
161
160
|
|
162
161
|
form_attributes :: Hash of attributes to use for any form tags
|
163
162
|
form_options :: Hash of Forme::Form options to pass for any forms created
|
164
|
-
class_display_name :: The string to use when referring to the model class
|
163
|
+
class_display_name :: The string to use on pages when referring to the model class.
|
164
|
+
This defaults to the full class name.
|
165
165
|
display_name :: The string to use when referring to a model instance. Can either be a symbol
|
166
166
|
representing an instance method call, or a Proc called with the model object,
|
167
167
|
the model object and type symbol, or the model object, type symbol, and request,
|
168
168
|
depending on the arity of the Proc.
|
169
|
-
link_name :: The string to use in links for the class
|
169
|
+
link_name :: The string to use in links for the class. This defaults to +class_display_name+.
|
170
170
|
edit_html :: The html to use for a particular object edit field. Should be a proc that takes the
|
171
171
|
model object, column symbol, type symbol, and request and returns the html to use.
|
172
172
|
page_footer :: Override the default footer used for pages
|
@@ -174,6 +174,8 @@ page_header :: Override the default header used for pages
|
|
174
174
|
show_html :: The html to use for displaying the value for an object field. Should be a proc that takes the
|
175
175
|
model object, column symbol, type symbol, and request and returns the html to use.
|
176
176
|
table_class :: The html class string to use for the browse and search tables
|
177
|
+
view_options :: Hash with options passed when rendering the view (how these options are used varies
|
178
|
+
in each of the supported web frameworks), e.g. <tt>view_options: {:layout => :formelayout}</tt>
|
177
179
|
|
178
180
|
These hook options should be callable objects that are called with the model object and the request.
|
179
181
|
|
@@ -238,6 +240,66 @@ use AutoForme in Rails development mode. The best way to handle it is to call
|
|
238
240
|
+AutoForme.for+ in the related controller file, and have an initializer reference
|
239
241
|
the controller class, causing the controller file to be loaded.
|
240
242
|
|
243
|
+
= Roda
|
244
|
+
|
245
|
+
Because Roda uses a routing tree, unlike Rails and Sinatra, with Roda you need to
|
246
|
+
dispatch to the autoforme routes at the point in the routing tree where you want
|
247
|
+
to mount them. Additionally, the Roda support offers a Roda plugin for easier
|
248
|
+
configuration.
|
249
|
+
|
250
|
+
To mount the autoforme routes in the root of the application, you could do:
|
251
|
+
|
252
|
+
class App < Roda
|
253
|
+
plugin :autoforme do
|
254
|
+
model Artist
|
255
|
+
end
|
256
|
+
|
257
|
+
route do
|
258
|
+
# rest of routing tree
|
259
|
+
autoforme
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
To mount the routes in a subpath:
|
264
|
+
|
265
|
+
class App < Roda
|
266
|
+
plugin :autoforme do
|
267
|
+
model Artist
|
268
|
+
end
|
269
|
+
|
270
|
+
route do
|
271
|
+
r.on "admin" do
|
272
|
+
autoforme
|
273
|
+
end
|
274
|
+
|
275
|
+
# rest of routing tree
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
To handle multiple autoforme configurations, mounted at different subpaths:
|
280
|
+
|
281
|
+
class App < Roda
|
282
|
+
plugin :autoforme
|
283
|
+
|
284
|
+
autoforme(name: 'artists')
|
285
|
+
model Artist
|
286
|
+
end
|
287
|
+
autoforme(name: 'albums')
|
288
|
+
model Album
|
289
|
+
end
|
290
|
+
|
291
|
+
route do
|
292
|
+
r.on "artists" do
|
293
|
+
autoforme('artists')
|
294
|
+
end
|
295
|
+
r.on "albums" do
|
296
|
+
autoforme('albums')
|
297
|
+
end
|
298
|
+
|
299
|
+
# rest of routing tree
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
241
303
|
= TODO
|
242
304
|
|
243
305
|
* capybara-webkit tests for ajax behavior
|
data/lib/autoforme/action.rb
CHANGED
@@ -87,7 +87,7 @@ module AutoForme
|
|
87
87
|
else
|
88
88
|
return false unless model.supported_action?(normalized_type, request)
|
89
89
|
|
90
|
-
if title = TITLE_MAP[
|
90
|
+
if title = TITLE_MAP[@normalized_type]
|
91
91
|
@title = "#{model.class_name} - #{title}"
|
92
92
|
end
|
93
93
|
end
|
@@ -227,10 +227,15 @@ module AutoForme
|
|
227
227
|
# Options to use for the form. If the form uses POST, automatically adds the CSRF token.
|
228
228
|
def form_opts(action=nil)
|
229
229
|
opts = model.form_options_for(type, request).dup
|
230
|
-
|
231
|
-
|
232
|
-
|
230
|
+
|
231
|
+
opts[:_before_post] = lambda do |form|
|
232
|
+
if csrf_hash = request.csrf_token_hash(action)
|
233
|
+
csrf_hash.each do |name, value|
|
234
|
+
form.tag(:input, :type=>:hidden, :name=>name, :value=>value)
|
235
|
+
end
|
236
|
+
end
|
233
237
|
end
|
238
|
+
|
234
239
|
opts
|
235
240
|
end
|
236
241
|
|
@@ -626,13 +631,13 @@ module AutoForme
|
|
626
631
|
# page.
|
627
632
|
def association_link(mc, assoc_obj)
|
628
633
|
if mc
|
629
|
-
t = mc.object_display_name(:association, request, assoc_obj)
|
634
|
+
t = h(mc.object_display_name(:association, request, assoc_obj))
|
630
635
|
if mc.supported_action?(type, request)
|
631
636
|
t = "<a href=\"#{base_url_for("#{mc.link}/#{type}/#{mc.primary_key_value(assoc_obj)}")}\">#{t}</a>"
|
632
637
|
end
|
633
638
|
t
|
634
639
|
else
|
635
|
-
model.default_object_display_name(assoc_obj)
|
640
|
+
h(model.default_object_display_name(assoc_obj))
|
636
641
|
end
|
637
642
|
end
|
638
643
|
|
@@ -58,7 +58,11 @@ module AutoForme
|
|
58
58
|
elsif @autoforme_action.request.xhr?
|
59
59
|
render :html=>@autoforme_text.html_safe
|
60
60
|
else
|
61
|
-
|
61
|
+
opts = framework.opts[:view_options]
|
62
|
+
opts = opts ? opts.dup : {}
|
63
|
+
opts[:layout] = true unless opts.has_key?(:layout)
|
64
|
+
opts[:inline] = "<%=raw @autoforme_text %>"
|
65
|
+
render opts
|
62
66
|
end
|
63
67
|
else
|
64
68
|
render :plain=>'Unhandled Request', :status=>404
|
@@ -37,22 +37,42 @@ module AutoForme
|
|
37
37
|
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
|
38
38
|
end
|
39
39
|
|
40
|
+
# Set the flash at notice level when redirecting, so it shows
|
41
|
+
# up on the redirected page.
|
42
|
+
def set_flash_notice(message)
|
43
|
+
@controller.flash[flash_symbol_keys? ? :notice : 'notice'] = message
|
44
|
+
end
|
45
|
+
|
46
|
+
# Set the current flash at error level, used when displaying
|
47
|
+
# pages when there is an error.
|
48
|
+
def set_flash_now_error(message)
|
49
|
+
@controller.flash.now[flash_symbol_keys? ? :error : 'error'] = message
|
50
|
+
end
|
51
|
+
|
40
52
|
# Use Rack::Csrf for csrf protection if it is defined.
|
41
53
|
def csrf_token_hash(action=nil)
|
42
54
|
if @controller.respond_to?(:check_csrf!)
|
43
55
|
# Using route_csrf plugin
|
44
|
-
# :nocov:
|
45
56
|
token = if @controller.use_request_specific_csrf_tokens?
|
46
57
|
@controller.csrf_token(@controller.csrf_path(action))
|
58
|
+
# :nocov:
|
47
59
|
else
|
48
60
|
@controller.csrf_token
|
61
|
+
# :nocov:
|
49
62
|
end
|
50
63
|
{@controller.csrf_field=>token}
|
51
64
|
# :nocov:
|
52
65
|
elsif defined?(::Rack::Csrf)
|
53
66
|
{::Rack::Csrf.field=>::Rack::Csrf.token(@env)}
|
67
|
+
# :nocov:
|
54
68
|
end
|
55
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def flash_symbol_keys?
|
74
|
+
!@controller.opts[:sessions_convert_symbols]
|
75
|
+
end
|
56
76
|
end
|
57
77
|
|
58
78
|
attr_reader :route_proc
|
@@ -88,7 +108,10 @@ module AutoForme
|
|
88
108
|
elsif @autoforme_action.request.xhr?
|
89
109
|
@autoforme_text
|
90
110
|
else
|
91
|
-
|
111
|
+
opts = framework.opts[:view_options]
|
112
|
+
opts = opts ? opts.dup : {}
|
113
|
+
opts[:content] = @autoforme_text
|
114
|
+
view(opts)
|
92
115
|
end
|
93
116
|
end
|
94
117
|
end
|
@@ -50,7 +50,9 @@ module AutoForme
|
|
50
50
|
elsif @autoforme_action.request.xhr?
|
51
51
|
@autoforme_text
|
52
52
|
else
|
53
|
-
|
53
|
+
opts = framework.opts[:view_options]
|
54
|
+
opts = opts ? opts.dup : {}
|
55
|
+
erb("<%= @autoforme_text %>".dup, opts ? opts.dup : {})
|
54
56
|
end
|
55
57
|
else
|
56
58
|
pass
|
data/lib/autoforme/model.rb
CHANGED
@@ -98,14 +98,26 @@ module AutoForme
|
|
98
98
|
ref[:keys].zip(ref[:primary_keys].map{|k| obj.send(k)})
|
99
99
|
end
|
100
100
|
|
101
|
+
# Array of many to many association name strings for editable
|
102
|
+
# many to many associations.
|
103
|
+
def editable_mtm_association_names
|
104
|
+
association_names([:many_to_many]) do |r|
|
105
|
+
model.method_defined?(r.add_method) && model.method_defined?(r.remove_method)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
101
109
|
# Array of many to many association name strings.
|
102
110
|
def mtm_association_names
|
103
111
|
association_names([:many_to_many])
|
104
112
|
end
|
105
113
|
|
106
|
-
# Array of association name strings for given association types
|
114
|
+
# Array of association name strings for given association types. If a block is
|
115
|
+
# given, only include associations where the block returns truthy.
|
107
116
|
def association_names(types=SUPPORTED_ASSOCIATION_TYPES)
|
108
|
-
model.all_association_reflections.
|
117
|
+
model.all_association_reflections.
|
118
|
+
select{|r| types.include?(r[:type]) && (!block_given? || yield(r))}.
|
119
|
+
map{|r| r[:name]}.
|
120
|
+
sort_by(&:to_s)
|
109
121
|
end
|
110
122
|
|
111
123
|
# Save the object, returning the object if successful, or nil if not.
|
@@ -158,7 +170,7 @@ module AutoForme
|
|
158
170
|
params = request.params
|
159
171
|
ds = apply_associated_eager(:search, request, all_dataset_for(type, request))
|
160
172
|
columns_for(:search_form, request).each do |c|
|
161
|
-
if (v = params[c.to_s]) && !v.empty?
|
173
|
+
if (v = params[c.to_s]) && !(v = v.to_s).empty?
|
162
174
|
if association?(c)
|
163
175
|
ref = model.association_reflection(c)
|
164
176
|
ads = ref.associated_dataset
|
@@ -168,9 +180,16 @@ module AutoForme
|
|
168
180
|
primary_key = S.qualify(ref.associated_class.table_name, ref.primary_key)
|
169
181
|
ds = ds.where(S.qualify(model.table_name, ref[:key])=>ads.where(primary_key=>v).select(primary_key))
|
170
182
|
elsif column_type(c) == :string
|
171
|
-
ds = ds.where(S.ilike(S.qualify(model.table_name, c), "%#{ds.escape_like(v
|
183
|
+
ds = ds.where(S.ilike(S.qualify(model.table_name, c), "%#{ds.escape_like(v)}%"))
|
172
184
|
else
|
173
|
-
|
185
|
+
begin
|
186
|
+
typecasted_value = model.db.typecast_value(column_type(c), v)
|
187
|
+
rescue S::InvalidValue
|
188
|
+
ds = ds.where(false)
|
189
|
+
break
|
190
|
+
else
|
191
|
+
ds = ds.where(S.qualify(model.table_name, c)=>typecasted_value)
|
192
|
+
end
|
174
193
|
end
|
175
194
|
end
|
176
195
|
end
|
@@ -293,7 +312,17 @@ module AutoForme
|
|
293
312
|
ids.each do |id|
|
294
313
|
next if id.to_s.empty?
|
295
314
|
ret = assoc_class ? assoc_class.with_pk(:association, request, id) : obj.send(:_apply_association_options, ref, ref.associated_class.dataset.clone).with_pk!(id)
|
296
|
-
|
315
|
+
begin
|
316
|
+
model.db.transaction(:savepoint=>true){obj.send(meth, ret)}
|
317
|
+
rescue S::UniqueConstraintViolation
|
318
|
+
# Already added, safe to ignore
|
319
|
+
rescue S::ConstraintViolation
|
320
|
+
# Old versions of sqlite3 and jdbc-sqlite3 can raise generic
|
321
|
+
# ConstraintViolation instead of UniqueConstraintViolation
|
322
|
+
# :nocov:
|
323
|
+
raise unless model.db.database_type == :sqlite
|
324
|
+
# :nocov:
|
325
|
+
end
|
297
326
|
end
|
298
327
|
end
|
299
328
|
end
|
data/lib/autoforme/version.rb
CHANGED
data/lib/autoforme.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
3
|
require 'forme'
|
4
|
-
require 'thread'
|
5
4
|
require 'rack/utils'
|
6
5
|
|
7
6
|
module AutoForme
|
@@ -50,10 +49,10 @@ module AutoForme
|
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
require_relative 'autoforme/opts_attributes'
|
53
|
+
require_relative 'autoforme/model'
|
54
|
+
require_relative 'autoforme/framework'
|
55
|
+
require_relative 'autoforme/request'
|
56
|
+
require_relative 'autoforme/action'
|
57
|
+
require_relative 'autoforme/table'
|
58
|
+
require_relative 'autoforme/version'
|
data/spec/all.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
puts "Running specs with #{ENV['FRAMEWORK']||'roda'} framework"
|
2
|
-
Dir
|
2
|
+
Dir.new(File.dirname(__FILE__)).each{|f| require_relative f if f.end_with?('_spec.rb')}
|
data/spec/associations_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
describe AutoForme do
|
4
4
|
before(:all) do
|
@@ -51,6 +51,50 @@ describe AutoForme do
|
|
51
51
|
page.all('td').map{|s| s.text}.must_equal ["Album1b", "Artist2", "Show", "Edit", "Delete"]
|
52
52
|
end
|
53
53
|
|
54
|
+
it "should escape display names in association links" do
|
55
|
+
app_setup do
|
56
|
+
model Artist
|
57
|
+
model Album do
|
58
|
+
columns [:name, :artist]
|
59
|
+
end
|
60
|
+
association_links :all
|
61
|
+
end
|
62
|
+
|
63
|
+
visit("/Artist/new")
|
64
|
+
fill_in 'Name', :with=>'Art&"ist2'
|
65
|
+
click_button 'Create'
|
66
|
+
|
67
|
+
visit("/Album/new")
|
68
|
+
fill_in 'Name', :with=>'Album1'
|
69
|
+
select 'Art&"ist2'
|
70
|
+
click_button 'Create'
|
71
|
+
|
72
|
+
click_link 'Edit'
|
73
|
+
select 'Album1'
|
74
|
+
click_button 'Edit'
|
75
|
+
page.html.must_match(%r{- <a href="/Artist/edit/\d+">Art&"ist2})
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should escape display names in association links" do
|
79
|
+
app_setup do
|
80
|
+
model Album do
|
81
|
+
columns [:name, :artist]
|
82
|
+
end
|
83
|
+
association_links :all
|
84
|
+
end
|
85
|
+
|
86
|
+
Artist.create(:name=>'Art&"ist2')
|
87
|
+
visit("/Album/new")
|
88
|
+
fill_in 'Name', :with=>'Album1'
|
89
|
+
select 'Art&"ist2'
|
90
|
+
click_button 'Create'
|
91
|
+
|
92
|
+
click_link 'Edit'
|
93
|
+
select 'Album1'
|
94
|
+
click_button 'Edit'
|
95
|
+
page.html.must_include("- Art&"ist2")
|
96
|
+
end
|
97
|
+
|
54
98
|
it "should use text boxes for associated objects on new/edit/search forms if associated model uses autocompleting" do
|
55
99
|
app_setup do
|
56
100
|
model Artist do
|
data/spec/basic_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
describe AutoForme do
|
4
4
|
before(:all) do
|
@@ -12,6 +12,7 @@ describe AutoForme do
|
|
12
12
|
it "should have basic functionality working" do
|
13
13
|
app_setup(Artist)
|
14
14
|
visit("/Artist/new")
|
15
|
+
page.html.must_include '<!DOCTYPE html>'
|
15
16
|
page.title.must_equal 'Artist - New'
|
16
17
|
fill_in 'Name', :with=>'TestArtistNew'
|
17
18
|
click_button 'Create'
|
@@ -573,7 +574,7 @@ describe AutoForme do
|
|
573
574
|
page.all('td').map{|s| s.text}.must_equal []
|
574
575
|
end
|
575
576
|
|
576
|
-
it "should
|
577
|
+
it "should correctly handle validation errors" do
|
577
578
|
app_setup(Artist)
|
578
579
|
Artist.send(:define_method, :validate) do
|
579
580
|
errors.add(:name, "bad name") if name == 'Foo'
|
@@ -583,6 +584,7 @@ describe AutoForme do
|
|
583
584
|
page.title.must_equal 'Artist - New'
|
584
585
|
fill_in 'Name', :with=>'Foo'
|
585
586
|
click_button 'Create'
|
587
|
+
page.title.must_equal 'Artist - New'
|
586
588
|
page.html.must_include 'Error Creating Artist'
|
587
589
|
page.html.must_include 'bad name'
|
588
590
|
fill_in 'Name', :with=>'TestArtistNew'
|
@@ -597,6 +599,7 @@ describe AutoForme do
|
|
597
599
|
click_button 'Edit'
|
598
600
|
fill_in 'Name', :with=>'Foo'
|
599
601
|
click_button 'Update'
|
602
|
+
page.title.must_equal 'Artist - Edit'
|
600
603
|
page.html.must_include 'Error Updating Artist'
|
601
604
|
page.html.must_include 'bad name'
|
602
605
|
fill_in 'Name', :with=>'TestArtistUpdate'
|
@@ -605,6 +608,13 @@ describe AutoForme do
|
|
605
608
|
page.html.must_match(/Name.+TestArtistUpdate/m)
|
606
609
|
page.current_path.must_match %r{/Artist/edit/\d+}
|
607
610
|
end
|
611
|
+
|
612
|
+
it "should support view_options" do
|
613
|
+
app_setup(Artist, view_options: {:layout=>false}){}
|
614
|
+
visit("/Artist/browse")
|
615
|
+
page.html.wont_include '<!DOCTYPE html>'
|
616
|
+
page.all('table').first['id'].must_equal 'autoforme_table'
|
617
|
+
end
|
608
618
|
end
|
609
619
|
|
610
620
|
describe AutoForme do
|
@@ -952,19 +962,29 @@ describe AutoForme do
|
|
952
962
|
after(:all) do
|
953
963
|
Object.send(:remove_const, :Artist)
|
954
964
|
end
|
955
|
-
|
956
|
-
it "should display decimals in float format in tables" do
|
965
|
+
before do
|
957
966
|
app_setup(Artist)
|
958
967
|
visit("/Artist/new")
|
959
968
|
page.title.must_equal 'Artist - New'
|
960
969
|
fill_in 'Num', :with=>'1.01'
|
961
970
|
click_button 'Create'
|
971
|
+
visit("/Artist/browse")
|
972
|
+
end
|
973
|
+
|
974
|
+
it "should display decimals in float format in tables" do
|
962
975
|
click_link 'Artist'
|
963
976
|
page.all('tr td:first-child').map{|s| s.text}.must_equal %w'1.01'
|
964
977
|
click_link 'Search'
|
965
978
|
click_button 'Search'
|
966
979
|
page.all('tr td:first-child').map{|s| s.text}.must_equal %w'1.01'
|
967
980
|
end
|
981
|
+
|
982
|
+
it "should treat invalid search fields as returning no results" do
|
983
|
+
click_link 'Search'
|
984
|
+
fill_in 'Num', :with=>'3/3/2020'
|
985
|
+
click_button 'Search'
|
986
|
+
page.all('tr td:first-child').map{|s| s.text}.must_equal []
|
987
|
+
end
|
968
988
|
end
|
969
989
|
|
970
990
|
describe AutoForme do
|
data/spec/mtm_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
describe AutoForme do
|
4
4
|
before(:all) do
|
@@ -507,3 +507,115 @@ describe AutoForme do
|
|
507
507
|
page.all('select')[1].all('option').map{|s| s.text}.must_equal ["Album2", "Album3"]
|
508
508
|
end
|
509
509
|
end
|
510
|
+
|
511
|
+
describe AutoForme do
|
512
|
+
before(:all) do
|
513
|
+
db_setup(:artists=>[[:name, :string]], :albums=>[[:name, :string]], :albums_artists=>proc{column :album_id, :integer, :table=>:albums; column :artist_id, :integer, :table=>:artists; primary_key [:album_id, :artist_id]})
|
514
|
+
model_setup(:Artist=>[:artists, [[:many_to_many, :albums]]], :Album=>[:albums, [[:many_to_many, :artists]]])
|
515
|
+
end
|
516
|
+
after(:all) do
|
517
|
+
Object.send(:remove_const, :Album)
|
518
|
+
Object.send(:remove_const, :Artist)
|
519
|
+
end
|
520
|
+
|
521
|
+
it "should handle unique constraint violation errors when adding associated objects" do
|
522
|
+
app_setup do
|
523
|
+
model Artist do
|
524
|
+
mtm_associations :albums
|
525
|
+
end
|
526
|
+
model Album
|
527
|
+
end
|
528
|
+
|
529
|
+
artist = Artist.create(:name=>'Artist1')
|
530
|
+
album = Album.create(:name=>'Album1')
|
531
|
+
|
532
|
+
visit("/Artist/mtm_edit")
|
533
|
+
page.title.must_equal 'Artist - Many To Many Edit'
|
534
|
+
select("Artist1")
|
535
|
+
click_button "Edit"
|
536
|
+
|
537
|
+
find('h2').text.must_equal 'Edit Albums for Artist1'
|
538
|
+
page.all('select')[0].all('option').map{|s| s.text}.must_equal ["Album1"]
|
539
|
+
page.all('select')[1].all('option').map{|s| s.text}.must_equal []
|
540
|
+
select("Album1", :from=>"Associate With")
|
541
|
+
artist.add_album(album)
|
542
|
+
click_button "Update"
|
543
|
+
page.html.must_include 'Updated albums association for Artist'
|
544
|
+
Artist.first.albums.map{|x| x.name}.must_equal %w'Album1'
|
545
|
+
end
|
546
|
+
|
547
|
+
it "should handle unique constraint violation errors when adding associated objects" do
|
548
|
+
app_setup do
|
549
|
+
model Artist do
|
550
|
+
mtm_associations :albums
|
551
|
+
end
|
552
|
+
model Album
|
553
|
+
end
|
554
|
+
|
555
|
+
artist = Artist.create(:name=>'Artist1')
|
556
|
+
album = Album.create(:name=>'Album1')
|
557
|
+
artist.add_album(album)
|
558
|
+
|
559
|
+
visit("/Artist/mtm_edit")
|
560
|
+
page.title.must_equal 'Artist - Many To Many Edit'
|
561
|
+
select("Artist1")
|
562
|
+
click_button "Edit"
|
563
|
+
|
564
|
+
find('h2').text.must_equal 'Edit Albums for Artist1'
|
565
|
+
page.all('select')[0].all('option').map{|s| s.text}.must_equal []
|
566
|
+
page.all('select')[1].all('option').map{|s| s.text}.must_equal ["Album1"]
|
567
|
+
select("Album1", :from=>"Disassociate From")
|
568
|
+
artist.remove_album(album)
|
569
|
+
click_button "Update"
|
570
|
+
page.html.must_include 'Updated albums association for Artist'
|
571
|
+
Artist.first.albums.map{|x| x.name}.must_equal []
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
describe AutoForme do
|
576
|
+
before(:all) do
|
577
|
+
db_setup(:artists=>[[:name, :string]], :albums=>[[:name, :string]], :albums_artists=>[[:album_id, :integer, {:table=>:albums}], [:artist_id, :integer, {:table=>:artists}]])
|
578
|
+
model_setup(:Artist=>[:artists, [[:many_to_many, :albums, :read_only=>true]]], :Album=>[:albums, [[:many_to_many, :artists, :read_only=>true]]])
|
579
|
+
end
|
580
|
+
after(:all) do
|
581
|
+
Object.send(:remove_const, :Album)
|
582
|
+
Object.send(:remove_const, :Artist)
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should not automatically setup mtm support for read-only associations" do
|
586
|
+
app_setup do
|
587
|
+
model Artist do
|
588
|
+
mtm_associations :all
|
589
|
+
association_links :all
|
590
|
+
end
|
591
|
+
model Album do
|
592
|
+
mtm_associations :all
|
593
|
+
association_links :all
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
visit("/Artist/new")
|
598
|
+
page.html.wont_include 'Artist/mtm_edit'
|
599
|
+
fill_in 'Name', :with=>'Artist1'
|
600
|
+
click_button 'Create'
|
601
|
+
click_link 'Edit'
|
602
|
+
select 'Artist1'
|
603
|
+
click_button 'Edit'
|
604
|
+
page.html.must_include 'Albums'
|
605
|
+
page.html.wont_include '>associate<'
|
606
|
+
visit("/Artist/mtm_edit")
|
607
|
+
page.html.must_include 'Unhandled Request'
|
608
|
+
|
609
|
+
visit("/Album/new")
|
610
|
+
page.html.wont_include 'Album/mtm_edit'
|
611
|
+
fill_in 'Name', :with=>'Album1'
|
612
|
+
click_button 'Create'
|
613
|
+
click_link 'Edit'
|
614
|
+
select 'Album1'
|
615
|
+
click_button 'Edit'
|
616
|
+
page.html.must_include 'Artists'
|
617
|
+
page.html.wont_include '>associate<'
|
618
|
+
visit("/Album/mtm_edit")
|
619
|
+
page.html.must_include 'Unhandled Request'
|
620
|
+
end
|
621
|
+
end
|
data/spec/rails_spec_helper.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'rails'
|
2
2
|
require 'action_controller/railtie'
|
3
|
-
|
3
|
+
require_relative '../lib/autoforme'
|
4
4
|
|
5
5
|
class AutoFormeSpec::App
|
6
|
+
class << self
|
7
|
+
# Workaround for action_view railtie deleting the finalizer
|
8
|
+
attr_accessor :av_finalizer
|
9
|
+
end
|
10
|
+
|
6
11
|
def self.autoforme(klass=nil, opts={}, &block)
|
7
12
|
sc = Class.new(Rails::Application)
|
8
13
|
def sc.name
|
@@ -16,7 +21,7 @@ class AutoFormeSpec::App
|
|
16
21
|
resolver = Class.new(ActionView::Resolver)
|
17
22
|
resolver.class_eval do
|
18
23
|
template = ActionView::Template
|
19
|
-
|
24
|
+
code = (<<HTML)
|
20
25
|
<!DOCTYPE html>
|
21
26
|
<html>
|
22
27
|
<head><title><%= @autoforme_action.title if @autoforme_action %></title></head>
|
@@ -30,6 +35,11 @@ class AutoFormeSpec::App
|
|
30
35
|
<%= yield %>
|
31
36
|
</body></html>"
|
32
37
|
HTML
|
38
|
+
if Rails.version > '6'
|
39
|
+
t = [template.new(code, "layout", template.handler_for_extension(:erb), :virtual_path=>'layout', :format=>'erb', :locals=>[])]
|
40
|
+
else
|
41
|
+
t = [template.new(code, "layout", template.handler_for_extension(:erb), :virtual_path=>'layout', :format=>'erb', :updated_at=>Time.now)]
|
42
|
+
end
|
33
43
|
|
34
44
|
define_method(:find_templates){|*args| t}
|
35
45
|
end
|
@@ -57,19 +67,39 @@ HTML
|
|
57
67
|
get 'session/set', :controller=>'autoforme', :action=>'session_set'
|
58
68
|
end.inspect
|
59
69
|
config.secret_token = st if Rails.respond_to?(:version) && Rails.version < '5.2'
|
70
|
+
config.hosts << "www.example.com" if config.respond_to?(:hosts)
|
60
71
|
config.active_support.deprecation = :stderr
|
61
72
|
config.middleware.delete(ActionDispatch::ShowExceptions)
|
62
73
|
config.middleware.delete(Rack::Lock)
|
63
74
|
config.secret_key_base = st*15
|
64
75
|
config.eager_load = true
|
76
|
+
if Rails.version.start_with?('4')
|
77
|
+
# Work around issue in backported openssl environments where
|
78
|
+
# secret is 64 bytes intead of 32 bytes
|
79
|
+
require 'active_support/message_encryptor'
|
80
|
+
def (ActiveSupport::MessageEncryptor).new(secret, *signature_key_or_options)
|
81
|
+
obj = allocate
|
82
|
+
obj.send(:initialize, secret[0, 32], *signature_key_or_options)
|
83
|
+
obj
|
84
|
+
end
|
85
|
+
end
|
65
86
|
if Rails.version > '4.2'
|
66
87
|
config.action_dispatch.cookies_serializer = :json
|
67
88
|
end
|
68
89
|
if Rails.version > '5'
|
69
90
|
# Force Rails to dispatch to correct controller
|
70
91
|
ActionDispatch::Routing::RouteSet::Dispatcher.class_eval do
|
92
|
+
alias controller controller
|
71
93
|
define_method(:controller){|_| controller}
|
72
94
|
end
|
95
|
+
config.session_store :cookie_store, :key=>'_autoforme_test_session'
|
96
|
+
end
|
97
|
+
if Rails.version > '6'
|
98
|
+
if AutoFormeSpec::App.av_finalizer
|
99
|
+
config.action_view.finalize_compiled_template_methods = AutoFormeSpec::App.av_finalizer
|
100
|
+
else
|
101
|
+
AutoFormeSpec::App.av_finalizer = config.action_view.finalize_compiled_template_methods
|
102
|
+
end
|
73
103
|
end
|
74
104
|
initialize!
|
75
105
|
end
|
data/spec/roda_spec.rb
CHANGED
data/spec/roda_spec_helper.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'roda'
|
3
|
-
|
2
|
+
require_relative '../lib/autoforme'
|
4
3
|
require 'rack/csrf'
|
5
4
|
|
6
5
|
begin
|
7
|
-
require '
|
8
|
-
require 'tilt/erubis'
|
6
|
+
require 'tilt/erubi'
|
9
7
|
rescue LoadError
|
10
8
|
require 'tilt/erb'
|
11
9
|
end
|
@@ -14,23 +12,38 @@ class AutoFormeSpec::App < Roda
|
|
14
12
|
opts[:unsupported_block_result] = :raise
|
15
13
|
opts[:unsupported_matcher] = :raise
|
16
14
|
opts[:verbatim_string_matcher] = true
|
15
|
+
opts[:check_dynamic_arity] = opts[:check_arity] = :warn
|
17
16
|
|
18
17
|
LAYOUT = <<HTML
|
19
18
|
<!DOCTYPE html>
|
20
19
|
<html>
|
21
20
|
<head><title><%= @autoforme_action.title if @autoforme_action %></title></head>
|
22
21
|
<body>
|
23
|
-
<% if flash[:notice] %>
|
24
|
-
<div class="alert alert-success"><p><%=
|
22
|
+
<% if notice = opts[:sessions_convert_symbols] ? flash['notice'] : flash[:notice] %>
|
23
|
+
<div class="alert alert-success"><p><%= notice %></p></div>
|
25
24
|
<% end %>
|
26
|
-
<% if flash[:error] %>
|
27
|
-
<div class="alert alert-error"><p><%=
|
25
|
+
<% if error = opts[:sessions_convert_symbols] ? flash['error'] : flash[:error] %>
|
26
|
+
<div class="alert alert-error"><p><%= error %></p></div>
|
28
27
|
<% end %>
|
29
28
|
<%= yield %>
|
30
29
|
</body></html>"
|
31
30
|
HTML
|
32
31
|
|
33
|
-
|
32
|
+
plugin :flash
|
33
|
+
|
34
|
+
if defined?(Roda::RodaVersionNumber) && Roda::RodaVersionNumber >= 30100
|
35
|
+
if ENV['RODA_ROUTE_CSRF'] == '0'
|
36
|
+
require 'roda/session_middleware'
|
37
|
+
opts[:sessions_convert_symbols] = true
|
38
|
+
use RodaSessionMiddleware, :secret=>SecureRandom.random_bytes(64)
|
39
|
+
else
|
40
|
+
ENV['RODA_ROUTE_CSRF'] ||= '1'
|
41
|
+
plugin :sessions, :secret=>SecureRandom.random_bytes(64)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
use Rack::Session::Cookie, :secret => '1'
|
45
|
+
end
|
46
|
+
|
34
47
|
if ENV['RODA_ROUTE_CSRF'].to_i > 0
|
35
48
|
plugin :route_csrf, :require_request_specific_tokens=>ENV['RODA_ROUTE_CSRF'] == '1'
|
36
49
|
else
|
@@ -42,7 +55,6 @@ HTML
|
|
42
55
|
plugin :not_found do
|
43
56
|
'Unhandled Request'
|
44
57
|
end
|
45
|
-
plugin :flash
|
46
58
|
|
47
59
|
def self.autoforme(klass=nil, opts={}, &block)
|
48
60
|
sc = Class.new(self)
|
data/spec/sequel_spec_helper.rb
CHANGED
data/spec/sinatra_spec_helper.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
$: << File.expand_path(File.join(__FILE__, '../../lib'))
|
3
2
|
ENV['FRAMEWORK'] ||= 'roda'
|
4
3
|
|
@@ -19,7 +18,7 @@ if ENV['COVERAGE']
|
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
|
-
|
21
|
+
require_relative "#{ENV['FRAMEWORK']}_spec_helper"
|
23
22
|
|
24
23
|
require 'capybara'
|
25
24
|
require 'capybara/dsl'
|
@@ -27,7 +26,7 @@ require 'rack/test'
|
|
27
26
|
|
28
27
|
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
29
28
|
gem 'minitest'
|
30
|
-
require 'minitest/autorun'
|
29
|
+
require 'minitest/global_expectations/autorun'
|
31
30
|
require 'minitest/hooks/default'
|
32
31
|
|
33
32
|
if ENV['WARNING']
|
@@ -35,7 +34,7 @@ if ENV['WARNING']
|
|
35
34
|
Warning.ignore([:missing_ivar, :fixnum, :not_reached])
|
36
35
|
end
|
37
36
|
|
38
|
-
|
37
|
+
require_relative 'sequel_spec_helper'
|
39
38
|
|
40
39
|
class Minitest::HooksSpec
|
41
40
|
include Rack::Test::Methods
|
data/spec/unit_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: autoforme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: forme
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 2.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.1.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest-global_expectations
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: capybara
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -243,7 +257,12 @@ files:
|
|
243
257
|
homepage: http://github.com/jeremyevans/autoforme
|
244
258
|
licenses:
|
245
259
|
- MIT
|
246
|
-
metadata:
|
260
|
+
metadata:
|
261
|
+
bug_tracker_uri: https://github.com/jeremyevans/autoforme/issues
|
262
|
+
changelog_uri: http://autoforme.jeremyevans.net/files/CHANGELOG.html
|
263
|
+
documentation_uri: http://autoforme.jeremyevans.net
|
264
|
+
mailing_list_uri: https://github.com/jeremyevans/autoforme/discussions
|
265
|
+
source_code_uri: https://github.com/jeremyevans/autoforme
|
247
266
|
post_install_message:
|
248
267
|
rdoc_options:
|
249
268
|
- "--quiet"
|
@@ -259,15 +278,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
259
278
|
requirements:
|
260
279
|
- - ">="
|
261
280
|
- !ruby/object:Gem::Version
|
262
|
-
version:
|
281
|
+
version: 1.9.2
|
263
282
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
264
283
|
requirements:
|
265
284
|
- - ">="
|
266
285
|
- !ruby/object:Gem::Version
|
267
286
|
version: '0'
|
268
287
|
requirements: []
|
269
|
-
|
270
|
-
rubygems_version: 2.7.6
|
288
|
+
rubygems_version: 3.2.32
|
271
289
|
signing_key:
|
272
290
|
specification_version: 4
|
273
291
|
summary: Web Administrative Console for Roda/Sinatra/Rails and Sequel::Model
|