autoforme 1.13.0 → 1.14.0
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/CHANGELOG +10 -0
- data/README.rdoc +5 -0
- data/lib/autoforme/action.rb +30 -10
- data/lib/autoforme/framework.rb +10 -2
- data/lib/autoforme/frameworks/rails.rb +4 -1
- data/lib/autoforme/model.rb +15 -2
- data/lib/autoforme/models/sequel.rb +27 -6
- data/lib/autoforme/version.rb +1 -1
- data/lib/roda/plugins/autoforme.rb +9 -1
- metadata +4 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a736d797ecc713bf84bca96606007e8eb02c19e83b698365417afcc44fe20f6
|
4
|
+
data.tar.gz: bb6237775d2413f4d57e6d6c30e458f8b0037d65ea3b87db83bbfa63affae2e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 21db83b2b59a9b71679b21f5d1fffae43f7c73809e5ee3bfe7af70cb14faf768208c794b2adf27c20777f322ef2dfc15b352e88f34b32dc6ca91b036ad88d785
|
7
|
+
data.tar.gz: 8b0f1295ff5b59334658c34e4d90d80aff5c9778bf3717f7c43eabede1accc7cb95aafca9043ac2fb81eaf667b215bb7f40f45368cdf6c88ce44b8a15a98e202
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
=== 1.14.0 (2025-10-02)
|
2
|
+
|
3
|
+
* Support customizing the searching done for specific columns via column_search_filter (jeremyevans)
|
4
|
+
|
5
|
+
* Support autoforme_framework Roda class method in Roda plugin, to allow for reflection (jeremyevans)
|
6
|
+
|
7
|
+
* Omit invalid Next/Previous links instead of including them in a tag with a disabled attribute (jeremyevans)
|
8
|
+
|
9
|
+
* Support pagination via filtering instead of offset using pagination_strategy :filter (requires unambiguous order) (jeremyevans)
|
10
|
+
|
1
11
|
=== 1.13.0 (2024-07-10)
|
2
12
|
|
3
13
|
* Typecast primary key values before using them for lookup (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -132,6 +132,9 @@ autocomplete_options :: Enable autocompletion for this model, with the given
|
|
132
132
|
:limit :: The number of results to return
|
133
133
|
:filter :: Similar to callback, but overriding the default filter
|
134
134
|
(a case insensitive substring search on display)
|
135
|
+
column_search_filter :: This should be a Proc that accepts a dataset, column, value, and request,
|
136
|
+
and returns a filtered dataset. This allows for customizing the search filtering,
|
137
|
+
so you can use non-columns/non-associations on the search form.
|
135
138
|
eager :: Array of associations to eagerly load in separate queries
|
136
139
|
eager_graph :: Array of associations to eager load in the same query
|
137
140
|
(necessary if order or filter refers to them)
|
@@ -144,6 +147,8 @@ inline_mtm_associations :: Array of many to many association symbols to allow ed
|
|
144
147
|
lazy_load_association_links :: Whether to show the association links directly on the show/edit pages,
|
145
148
|
or to load them via ajax on request
|
146
149
|
mtm_associations :: Array of many to many association symbols to support editing on a separate page
|
150
|
+
pagination_strategy :: Set to :filter to paginate using a filter instead of using offsets. This requires
|
151
|
+
the column have an unambiguous order.
|
147
152
|
per_page :: Number of records to show per page on the browse and search pages
|
148
153
|
session_value :: Sets up a filter and before_create hook that makes it so access is limited
|
149
154
|
to objects where the object's column value is the same as the session value
|
data/lib/autoforme/action.rb
CHANGED
@@ -139,7 +139,6 @@ module AutoForme
|
|
139
139
|
end
|
140
140
|
|
141
141
|
request.redirect(path)
|
142
|
-
nil
|
143
142
|
end
|
144
143
|
|
145
144
|
# Handle the current action, returning an HTML string containing the page content,
|
@@ -415,20 +414,41 @@ module AutoForme
|
|
415
414
|
def table_pager(type, next_page)
|
416
415
|
html = String.new
|
417
416
|
html << '<ul class="pager">'
|
418
|
-
|
419
|
-
|
420
|
-
|
417
|
+
qs = next_qs = h request.query_string
|
418
|
+
|
419
|
+
if next_page.is_a?(Array)
|
420
|
+
# Filter pagination, next_page contains the values for the last record of current page
|
421
|
+
no_previous = true
|
422
|
+
if next_page.empty?
|
423
|
+
next_page = nil
|
424
|
+
else
|
425
|
+
next_qs = qs.dup
|
426
|
+
params = request.params.dup
|
427
|
+
next_page = next_page.map(&:to_s)
|
428
|
+
next_page = next_page[0] if next_page.length == 1
|
429
|
+
params["_after"] = next_page
|
430
|
+
next_qs = h Rack::Utils.build_nested_query(params)
|
431
|
+
following = prev = "0"
|
432
|
+
end
|
421
433
|
else
|
422
|
-
|
434
|
+
# Offset pagination, request id contains current page
|
435
|
+
page = request.id.to_i
|
436
|
+
page = 1 if page < 1
|
437
|
+
no_previous = page <= 1
|
438
|
+
prev = page-1
|
439
|
+
following = page+1
|
440
|
+
end
|
441
|
+
|
442
|
+
unless no_previous
|
443
|
+
html << "<li><a href=\"#{url_for("#{type}/#{prev}?#{qs}")}\">Previous</a></li>"
|
423
444
|
end
|
445
|
+
|
424
446
|
if next_page
|
425
|
-
|
426
|
-
html << "<li><a href=\"#{url_for("#{type}/#{page+1}?#{h request.query_string}")}\">Next</a></li>"
|
427
|
-
else
|
428
|
-
html << '<li class="disabled"><a href="#">Next</a></li>'
|
447
|
+
html << "<li><a href=\"#{url_for("#{type}/#{following}?#{next_qs}")}\">Next</a></li>"
|
429
448
|
end
|
449
|
+
|
430
450
|
html << "</ul>"
|
431
|
-
html << "<p><a href=\"#{url_for("#{type}/csv?#{
|
451
|
+
html << "<p><a href=\"#{url_for("#{type}/csv?#{qs}")}\">CSV Format</a></p>"
|
432
452
|
end
|
433
453
|
|
434
454
|
# Show page used for browse/search pages.
|
data/lib/autoforme/framework.rb
CHANGED
@@ -36,9 +36,9 @@ module AutoForme
|
|
36
36
|
|
37
37
|
opts_attribute :after_create, :after_destroy, :after_update, :association_links,
|
38
38
|
:autocomplete_options, :before_action, :before_create, :before_destroy,
|
39
|
-
:before_edit, :before_new, :before_update, :column_options,
|
39
|
+
:before_edit, :before_new, :before_update, :column_options, :column_search_filter,
|
40
40
|
:columns, :display_name, :filter, :form_attributes, :form_options,
|
41
|
-
:inline_mtm_associations, :lazy_load_association_links,
|
41
|
+
:inline_mtm_associations, :lazy_load_association_links, :pagination_strategy,
|
42
42
|
:model_type, :mtm_associations, :order, :page_footer, :page_header, :per_page,
|
43
43
|
:redirect, :supported_actions, :table_class, :show_html, :edit_html
|
44
44
|
|
@@ -118,6 +118,14 @@ module AutoForme
|
|
118
118
|
handle_proc(association_links, model, type, request)
|
119
119
|
end
|
120
120
|
|
121
|
+
def column_search_filter_for(model, dataset, column, value, request)
|
122
|
+
handle_proc(column_search_filter, model, dataset, column, value, request)
|
123
|
+
end
|
124
|
+
|
125
|
+
def pagination_strategy_for(model, type, request)
|
126
|
+
handle_proc(pagination_strategy, model, type, request)
|
127
|
+
end
|
128
|
+
|
121
129
|
def show_html_for(obj, column, type, request)
|
122
130
|
handle_proc(show_html, obj, column, type, request)
|
123
131
|
end
|
@@ -6,7 +6,10 @@ module AutoForme
|
|
6
6
|
class Request < AutoForme::Request
|
7
7
|
def initialize(request)
|
8
8
|
@controller = request
|
9
|
-
@params =
|
9
|
+
@params = {}
|
10
|
+
request.params.each do |k, v|
|
11
|
+
@params[k] = v
|
12
|
+
end
|
10
13
|
@session = request.session
|
11
14
|
@env = request.request.env
|
12
15
|
@method = @env['REQUEST_METHOD']
|
data/lib/autoforme/model.rb
CHANGED
@@ -18,6 +18,11 @@ module AutoForme
|
|
18
18
|
# Regexp for valid constant names, to prevent code execution.
|
19
19
|
VALID_CONSTANT_NAME_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.freeze
|
20
20
|
|
21
|
+
# The default pagination strategy to use. Offset is used by default,
|
22
|
+
# as a filter strategy requires an unambiguous order, which the library
|
23
|
+
# cannot guarantee.
|
24
|
+
DEFAULT_PAGINATION_STRATEGY = :offset
|
25
|
+
|
21
26
|
extend OptsAttributes
|
22
27
|
|
23
28
|
# Create a new instance for the given model type and underlying model class
|
@@ -38,9 +43,9 @@ module AutoForme
|
|
38
43
|
:autocomplete_options, :before_action, :before_create, :before_destroy,
|
39
44
|
:before_edit, :before_new, :before_update, :class_display_name,
|
40
45
|
:column_options, :columns, :display_name, :eager, :eager_graph,
|
41
|
-
:filter, :form_attributes, :form_options,
|
46
|
+
:filter, :form_attributes, :form_options, :column_search_filter,
|
42
47
|
:inline_mtm_associations, :lazy_load_association_links, :link_name, :mtm_associations,
|
43
|
-
:order, :page_footer, :page_header, :per_page,
|
48
|
+
:order, :page_footer, :page_header, :per_page, :pagination_strategy,
|
44
49
|
:redirect, :supported_actions, :table_class, :show_html, :edit_html
|
45
50
|
|
46
51
|
def initialize(model, framework)
|
@@ -95,6 +100,14 @@ module AutoForme
|
|
95
100
|
handle_proc(columns || framework.columns_for(model, type, request), type, request) || default_columns
|
96
101
|
end
|
97
102
|
|
103
|
+
def pagination_strategy_for(type, request)
|
104
|
+
handle_proc(pagination_strategy || framework.pagination_strategy_for(model, type, request), type, request) || DEFAULT_PAGINATION_STRATEGY
|
105
|
+
end
|
106
|
+
|
107
|
+
def column_search_filter_for(dataset, column, value, request)
|
108
|
+
handle_proc(column_search_filter || framework.column_search_filter_for(model, dataset, column, value, request), dataset, column, value, request)
|
109
|
+
end
|
110
|
+
|
98
111
|
# The options to use for the given column and request. Instead of the model options overriding the framework
|
99
112
|
# options, they are merged together.
|
100
113
|
def column_options_for(type, request, column)
|
@@ -177,7 +177,9 @@ module AutoForme
|
|
177
177
|
ds = apply_associated_eager(:search, request, all_dataset_for(type, request))
|
178
178
|
columns_for(:search_form, request).each do |c|
|
179
179
|
if (v = params[c.to_s]) && !(v = v.to_s).empty?
|
180
|
-
if
|
180
|
+
if filtered_ds = column_search_filter_for(ds, c, v, request)
|
181
|
+
ds = filtered_ds
|
182
|
+
elsif association?(c)
|
181
183
|
ref = model.association_reflection(c)
|
182
184
|
ads = ref.associated_dataset
|
183
185
|
if model_class = associated_model_class(c)
|
@@ -212,13 +214,32 @@ module AutoForme
|
|
212
214
|
def paginate(type, request, ds, opts={})
|
213
215
|
return ds.all if opts[:all_results]
|
214
216
|
limit = limit_for(type, request)
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
217
|
+
ds = ds.limit(limit+1)
|
218
|
+
|
219
|
+
if pagination_strategy_for(type, request) == :filter
|
220
|
+
order_cols = ds.send(:hash_key_symbols, Array(order_for(type, request)).map{|c| c.is_a?(S::SQL::OrderedExpression) ? c.expression : c})
|
221
|
+
after = Array(request.params["_after"])
|
222
|
+
if order_cols.length == after.length
|
223
|
+
begin
|
224
|
+
after = order_cols.zip(after).map{|c, v| typecast_value(c, v)}
|
225
|
+
rescue S::InvalidValue
|
226
|
+
# ignore pagination, assume first page
|
227
|
+
else
|
228
|
+
ds = ds.where(ds.send(:ignore_values_preceding, {}){after})
|
229
|
+
end
|
230
|
+
end
|
231
|
+
else # offset strategy, the default
|
232
|
+
%r{\/(\d+)\z} =~ request.env['PATH_INFO']
|
233
|
+
offset = (($1||1).to_i - 1) * limit
|
234
|
+
ds = ds.offset(offset) if offset > 0
|
235
|
+
end
|
236
|
+
|
237
|
+
objs = ds.all
|
238
|
+
next_page = order_cols && []
|
219
239
|
if objs.length > limit
|
220
|
-
next_page = true
|
221
240
|
objs.pop
|
241
|
+
last_obj = objs[-1]
|
242
|
+
next_page = after ? order_cols.map{|c| last_obj.send(c)} : true
|
222
243
|
end
|
223
244
|
[next_page, objs]
|
224
245
|
end
|
data/lib/autoforme/version.rb
CHANGED
@@ -14,6 +14,7 @@ class Roda
|
|
14
14
|
# the options and block.
|
15
15
|
def self.configure(app, opts={}, &block)
|
16
16
|
app.instance_exec do
|
17
|
+
@autoforme_frameworks ||= {}
|
17
18
|
@autoforme_routes ||= {}
|
18
19
|
if block
|
19
20
|
autoforme(opts, &block)
|
@@ -26,7 +27,13 @@ class Roda
|
|
26
27
|
# options and block. If the :name option is given, store
|
27
28
|
# this configuration for the given name.
|
28
29
|
def autoforme(opts={}, &block)
|
29
|
-
@
|
30
|
+
framework = @autoforme_frameworks[opts[:name]] = ::AutoForme.for(:roda, self, opts, &block)
|
31
|
+
@autoforme_routes[opts[:name]] = framework.route_proc
|
32
|
+
end
|
33
|
+
|
34
|
+
# Retrieve the framework the named or default AutoForme.
|
35
|
+
def autoforme_framework(name=nil)
|
36
|
+
@autoforme_frameworks[name]
|
30
37
|
end
|
31
38
|
|
32
39
|
# Retrieve the route proc for the named or default AutoForme.
|
@@ -37,6 +44,7 @@ class Roda
|
|
37
44
|
# Copy the autoforme configurations into the subclass.
|
38
45
|
def inherited(subclass)
|
39
46
|
super
|
47
|
+
subclass.instance_variable_set(:@autoforme_frameworks, @autoforme_frameworks.dup)
|
40
48
|
subclass.instance_variable_set(:@autoforme_routes, @autoforme_routes.dup)
|
41
49
|
end
|
42
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: autoforme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: forme
|
@@ -221,9 +220,9 @@ email: code@jeremyevans.net
|
|
221
220
|
executables: []
|
222
221
|
extensions: []
|
223
222
|
extra_rdoc_files:
|
224
|
-
- README.rdoc
|
225
223
|
- CHANGELOG
|
226
224
|
- MIT-LICENSE
|
225
|
+
- README.rdoc
|
227
226
|
files:
|
228
227
|
- CHANGELOG
|
229
228
|
- MIT-LICENSE
|
@@ -251,7 +250,6 @@ metadata:
|
|
251
250
|
documentation_uri: http://autoforme.jeremyevans.net
|
252
251
|
mailing_list_uri: https://github.com/jeremyevans/autoforme/discussions
|
253
252
|
source_code_uri: https://github.com/jeremyevans/autoforme
|
254
|
-
post_install_message:
|
255
253
|
rdoc_options:
|
256
254
|
- "--quiet"
|
257
255
|
- "--line-numbers"
|
@@ -273,8 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
273
271
|
- !ruby/object:Gem::Version
|
274
272
|
version: '0'
|
275
273
|
requirements: []
|
276
|
-
rubygems_version: 3.
|
277
|
-
signing_key:
|
274
|
+
rubygems_version: 3.6.9
|
278
275
|
specification_version: 4
|
279
276
|
summary: Web Administrative Console for Roda/Sinatra/Rails and Sequel::Model
|
280
277
|
test_files: []
|