recourse 1.1.0 → 1.2.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/README.md +1 -2
- data/app/controllers/recourses_controller.rb +26 -0
- data/app/helpers/recoursive_helper.rb +6 -13
- data/app/helpers/searchable_helper.rb +1 -1
- data/app/views/recourses/_row.html.erb +10 -0
- data/app/views/recourses/_table.html.erb +35 -0
- data/app/views/recourses/index.html.erb +30 -0
- data/lib/recourse/engine.rb +2 -1
- data/lib/recourse/{model/recoursive.rb → recoursive.rb} +6 -14
- data/lib/recourse/routing.rb +22 -10
- data/lib/recourse/{model/searchable.rb → searchable.rb} +4 -0
- data/lib/recourse/version.rb +1 -1
- data/lib/recourse.rb +0 -2
- metadata +7 -21
- data/app/controllers/recourse_controller.rb +0 -23
- data/app/views/recourse/_row.html.erb +0 -4
- data/app/views/recourse/index.html.erb +0 -21
- data/lib/recourse/model.rb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8ac6749b83bd677e0066b47d0e3137dc3c21d320bfef56e1df5857bb49fa425
|
|
4
|
+
data.tar.gz: 1bedace6c0dbfc04d6a7bb741e4f21999dd932b8e0d510e4057c75c394ddd36d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a834ed2627af083af2702471e8b2455c04b9750ef90e4bdd879283900ad7c2dc8a438237311e99768c9ddeee8f49024c17daf029e12aa137cd08423a0650efb
|
|
7
|
+
data.tar.gz: de580547ed1f66cc50fa6d4214ef665417bbf603989c75b045eb16a4499e8a66967d09e0ed5a1eefbfadd4ef89745e6f088992627f05dcbc1fc852a74f0739a5
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Provides a new `recourses` method that can be invoked in a Rails app inside conf
|
|
|
13
13
|
How to install
|
|
14
14
|
==============
|
|
15
15
|
|
|
16
|
-
1. Add `gem '
|
|
16
|
+
1. Add `gem 'recourse'` to the `Gemfile` file of your Rails app.
|
|
17
17
|
|
|
18
18
|
Available methods
|
|
19
19
|
=================
|
|
@@ -26,7 +26,6 @@ In a resourceful Active Record model:
|
|
|
26
26
|
|
|
27
27
|
- `recourse_includes`: the associations to include when fetching the resources
|
|
28
28
|
- `recourse_order`: the SQL to sort the resources by
|
|
29
|
-
- `recourse_positionable?`: whether the model has a position field to drag'n'drop sort
|
|
30
29
|
|
|
31
30
|
Anywhere:
|
|
32
31
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Base class for recoursive controllers.
|
|
2
|
+
class RecoursesController < ApplicationController
|
|
3
|
+
helper RecoursiveHelper, SearchableHelper
|
|
4
|
+
include Pagy::Method
|
|
5
|
+
|
|
6
|
+
def index
|
|
7
|
+
model = controller_name.classify.constantize
|
|
8
|
+
@where = request.path_parameters.except(:controller, :action)
|
|
9
|
+
@order = model.recourse_order
|
|
10
|
+
@includes = model.recourse_includes
|
|
11
|
+
|
|
12
|
+
@pagy, @resources = paginate model, where: @where, order: model.recourse_order, includes: model.recourse_includes
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def paginate(model, where: nil, includes: [], order: nil)
|
|
18
|
+
if model.recourse_searchable? || model.recourse_sortable?
|
|
19
|
+
@q = model.where(where).ransack params[:q]
|
|
20
|
+
@q.sorts = (order || 'created_at desc') if @q.sorts.empty?
|
|
21
|
+
pagy includes.present? ? @q.result.includes(includes) : @q.result.distinct(true)
|
|
22
|
+
else
|
|
23
|
+
pagy model.where(where).includes(includes).order(order)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
# A collection of helper methods used for navigation link in the admin-only section
|
|
2
2
|
module RecoursiveHelper
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def button_link_to(name, path, options = {})
|
|
11
|
-
link_to name, path, with_class('btn theme-primary', options)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def with_class(classes, options = {})
|
|
15
|
-
options.merge class: [options[:class], classes].compact.join(' ')
|
|
3
|
+
def column(header:, **options, &block)
|
|
4
|
+
if @recourse_headers
|
|
5
|
+
tag.th header, **options.merge(scope: :col)
|
|
6
|
+
else
|
|
7
|
+
tag.td **options, &block
|
|
8
|
+
end
|
|
16
9
|
end
|
|
17
10
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# A collection of helper methods used
|
|
1
|
+
# A collection of helper methods used to search resources with Ransack.
|
|
2
2
|
module SearchableHelper
|
|
3
3
|
# Renders a form with a field to search within +model+ that uses +placeholder+.
|
|
4
4
|
# @param [String] q the search query.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# locals: (recourse:) -%>
|
|
2
|
+
<% recourse.attributes.each do |name, value| %>
|
|
3
|
+
<%= column header: (recourse.class.ransortable_attributes.include?(name) ? sort_link(@q, name, name.upcase_first) : name) do %>
|
|
4
|
+
<% if recourse.class.ransackable_attributes.include? name %>
|
|
5
|
+
<%= search_highlight value, model: recourse.class %>
|
|
6
|
+
<% else %>
|
|
7
|
+
<%= value %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% end unless recourse.encrypted_attribute?(name) %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<%# locals: (recourses:, pagy: nil, cached: true) -%>
|
|
2
|
+
<% name = @lookup_context.find_template('row', @lookup_context.prefixes, true).short_identifier.split('/')[-2] %>
|
|
3
|
+
<% key = name.singularize.to_sym %>
|
|
4
|
+
|
|
5
|
+
<%= turbo_frame_tag :results, data: { turbo_action: :advance }, class: 'w-100' do %>
|
|
6
|
+
<% if recourses.empty? %>
|
|
7
|
+
<p>No <%= controller_path.split('/').last %>.</p>
|
|
8
|
+
<% else %>
|
|
9
|
+
<% cache_if cached, [recourses, (recourses.unscoped.size if cached)].compact do %>
|
|
10
|
+
<table class='table caption-top table-hover table-responsive'>
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<% @recourse_headers = true %>
|
|
14
|
+
<%= render 'row', key => recourses.first %>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody class='table-group-divider'>
|
|
18
|
+
<% @recourse_headers = false %>
|
|
19
|
+
<% recourses.each do |recourse| %>
|
|
20
|
+
<% cache_if (cached && !params.key?(:q)), recourse do %>
|
|
21
|
+
<tr><%= render 'row', key => recourse %></tr>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
</tbody>
|
|
25
|
+
</table>
|
|
26
|
+
|
|
27
|
+
<% if pagy %>
|
|
28
|
+
<div class='d-flex align-items-center'>
|
|
29
|
+
<div class='ps-2 flex-grow-1'><%== pagy.info_tag %></div>
|
|
30
|
+
<div class='pe-2'><%== pagy.series_nav(:bootstrap, classes: 'pagination mb-0') if pagy.last > 1 %></div>
|
|
31
|
+
</div>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% end %>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% resources = instance_variable_get("@#{controller_path.split('/').last}") || @resources %>
|
|
2
|
+
|
|
3
|
+
<% content_for :title, controller_path.split('/').last.humanize if @where&.empty? %>
|
|
4
|
+
|
|
5
|
+
<% model = controller_name.classify.constantize %>
|
|
6
|
+
|
|
7
|
+
<% content_for :search do %>
|
|
8
|
+
<script>
|
|
9
|
+
function debounce(method) {
|
|
10
|
+
let timer
|
|
11
|
+
return (...args) => {
|
|
12
|
+
clearTimeout(timer)
|
|
13
|
+
timer = setTimeout(() => { method.apply(this, args) }, 300);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const debouncedSubmit = debounce((form) => form.requestSubmit())
|
|
17
|
+
</script>
|
|
18
|
+
<%= search_form q: @q, model: model, url: request.path %>
|
|
19
|
+
<% end if model.recourse_searchable? %>
|
|
20
|
+
|
|
21
|
+
<% content_for :actions do %>
|
|
22
|
+
<% begin %>
|
|
23
|
+
<%= link_to "Add #{controller_path.split('/').last.singularize}", url_for(action: :new), class: 'btn theme-primary btn-sm btn-outline ms-3'%>
|
|
24
|
+
<% rescue ActionController::UrlGenerationError %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% end %>
|
|
27
|
+
|
|
28
|
+
<% locals = { @lookup_context.find_template('table', @lookup_context.prefixes, true).short_identifier.split('/')[-2].to_sym => resources } %>
|
|
29
|
+
|
|
30
|
+
<%= render 'table', locals.merge(pagy: @pagy, cached: model.recourse_cachable?) %>
|
data/lib/recourse/engine.rb
CHANGED
|
@@ -7,13 +7,6 @@ module Recourse
|
|
|
7
7
|
%i[]
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
# Return the actions available for the model (among :new, :edit, :destroy)
|
|
11
|
-
def recourse_actions
|
|
12
|
-
default = %i[new edit destroy]
|
|
13
|
-
options = Recourse.resources[name.underscore.pluralize.to_sym]
|
|
14
|
-
(Array(options.fetch :only, default) & default) - Array(options.fetch :except, [])
|
|
15
|
-
end
|
|
16
|
-
|
|
17
10
|
# Return the fields to .order when loading all the models.
|
|
18
11
|
def recourse_order
|
|
19
12
|
end
|
|
@@ -23,19 +16,18 @@ module Recourse
|
|
|
23
16
|
true
|
|
24
17
|
end
|
|
25
18
|
|
|
26
|
-
# Return whether the model has a position field to use for drag'n'drop sorting.
|
|
27
|
-
def recourse_positionable?
|
|
28
|
-
false
|
|
29
|
-
end
|
|
30
|
-
|
|
31
19
|
# @return [Boolean] whether Ransack search attributes are defined on the resource.
|
|
32
20
|
def recourse_searchable?
|
|
33
|
-
ransackable_attributes.any?
|
|
21
|
+
ransackable_attributes.any? || ransackable_associations.any?
|
|
34
22
|
end
|
|
35
23
|
|
|
36
24
|
# @return [Boolean] whether Ransack sort attributes are defined on the resource.
|
|
37
25
|
def recourse_sortable?
|
|
38
|
-
|
|
26
|
+
ransortable_attributes.any?
|
|
39
27
|
end
|
|
40
28
|
end
|
|
41
29
|
end
|
|
30
|
+
|
|
31
|
+
ActiveSupport.on_load(:active_record) do
|
|
32
|
+
extend Recourse::Recoursive
|
|
33
|
+
end
|
data/lib/recourse/routing.rb
CHANGED
|
@@ -4,20 +4,32 @@ module Recourse
|
|
|
4
4
|
module Routing
|
|
5
5
|
# This method is equivalent to Rails `resources` with the added bonus that we store
|
|
6
6
|
# the name of these administered resources so we can display them to admins in the navbar.
|
|
7
|
-
def recourses(*
|
|
8
|
-
|
|
7
|
+
def recourses(*args, **kwargs, &block)
|
|
8
|
+
store_recourses args, kwargs
|
|
9
|
+
resources(*args, **kwargs, &scoped(args, &block))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
administered_resource = administered_resources.sole
|
|
14
|
-
resources administered_resource, concerns: concerns, **options do
|
|
15
|
-
scope module: administered_resource, &block
|
|
16
|
-
end
|
|
14
|
+
def store_recourses(args, kwargs)
|
|
15
|
+
if @scope[:scope_level_resource]
|
|
16
|
+
Recourse.resources[@scope[:scope_level_resource].name.to_sym][:nested] = args
|
|
17
17
|
else
|
|
18
|
-
|
|
18
|
+
routes = Array(kwargs.fetch :only, self.class::Resource.default_actions(false))
|
|
19
|
+
routes-= Array(kwargs.fetch :except, [])
|
|
20
|
+
|
|
21
|
+
Recourse.resources.merge! args.to_h { |arg| [arg, { module: @scope[:module], routes: routes }] }
|
|
19
22
|
end
|
|
20
23
|
end
|
|
24
|
+
|
|
25
|
+
def scoped(args, &block)
|
|
26
|
+
return unless block_given?
|
|
27
|
+
|
|
28
|
+
parent_module = args.sole
|
|
29
|
+
Proc.new { scope module: parent_module, &block }
|
|
30
|
+
rescue Enumerable::SoleItemExpectedError
|
|
31
|
+
raise ArgumentError, 'recourses accepts only one resource if a block is given'
|
|
32
|
+
end
|
|
21
33
|
end
|
|
22
34
|
end
|
|
23
35
|
|
data/lib/recourse/version.rb
CHANGED
data/lib/recourse.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
require 'bh'
|
|
2
1
|
require 'pagy'
|
|
3
2
|
require 'ransack'
|
|
4
3
|
require 'recourse/engine'
|
|
5
4
|
|
|
6
5
|
# @see https://ddnexus.github.io/pagy/resources/initializer/
|
|
7
6
|
Pagy::OPTIONS[:limit] = 15
|
|
8
|
-
# Pagy.options[:limit] = 15
|
|
9
7
|
|
|
10
8
|
# A module to manage administered resources.
|
|
11
9
|
module Recourse
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: recourse
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Claudio Baccigalupo
|
|
@@ -23,20 +23,6 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: bh
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: pagy
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -103,18 +89,18 @@ files:
|
|
|
103
89
|
- MIT-LICENSE
|
|
104
90
|
- README.md
|
|
105
91
|
- Rakefile
|
|
106
|
-
- app/controllers/
|
|
92
|
+
- app/controllers/recourses_controller.rb
|
|
107
93
|
- app/helpers/recoursive_helper.rb
|
|
108
94
|
- app/helpers/searchable_helper.rb
|
|
109
|
-
- app/views/
|
|
110
|
-
- app/views/
|
|
95
|
+
- app/views/recourses/_row.html.erb
|
|
96
|
+
- app/views/recourses/_table.html.erb
|
|
97
|
+
- app/views/recourses/index.html.erb
|
|
111
98
|
- db/migrate/20260323234318_add_baby_to_posts.rb
|
|
112
99
|
- lib/recourse.rb
|
|
113
100
|
- lib/recourse/engine.rb
|
|
114
|
-
- lib/recourse/
|
|
115
|
-
- lib/recourse/model/recoursive.rb
|
|
116
|
-
- lib/recourse/model/searchable.rb
|
|
101
|
+
- lib/recourse/recoursive.rb
|
|
117
102
|
- lib/recourse/routing.rb
|
|
103
|
+
- lib/recourse/searchable.rb
|
|
118
104
|
- lib/recourse/version.rb
|
|
119
105
|
homepage: https://github.com/claudiob/recourse
|
|
120
106
|
licenses:
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Base class for controllers that require admin access
|
|
2
|
-
class RecourseController < ApplicationController
|
|
3
|
-
helper RecoursiveHelper, SearchableHelper
|
|
4
|
-
include Pagy::Method
|
|
5
|
-
|
|
6
|
-
def index
|
|
7
|
-
@model = controller_name.classify.constantize
|
|
8
|
-
@pagy, @resources = paginate @model, order: @model.recourse_order, includes: @model.recourse_includes
|
|
9
|
-
render 'recourse/index'
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def paginate(model, includes: [], order: nil)
|
|
15
|
-
if model.recourse_searchable? || model.recourse_sortable?
|
|
16
|
-
@q = model.ransack params[:q]
|
|
17
|
-
@q.sorts = (order || 'created_at desc') if @q.sorts.empty?
|
|
18
|
-
pagy includes.present? ? @q.result.includes(includes) : @q.result.distinct(true)
|
|
19
|
-
else
|
|
20
|
-
pagy @model.includes(includes).order(order)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
<% content_for :title, @model.name.pluralize %>
|
|
2
|
-
|
|
3
|
-
<% content_for :search do %>
|
|
4
|
-
<script>
|
|
5
|
-
function debounce(method) {
|
|
6
|
-
let timer
|
|
7
|
-
return (...args) => {
|
|
8
|
-
clearTimeout(timer)
|
|
9
|
-
timer = setTimeout(() => { method.apply(this, args) }, 300);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
const debouncedSubmit = debounce((form) => form.requestSubmit())
|
|
13
|
-
</script>
|
|
14
|
-
<%= search_form q: @q, model: @model %>
|
|
15
|
-
<% end if @model.recourse_searchable? %>
|
|
16
|
-
|
|
17
|
-
<% button_link_to_add @model.name.underscore, url_for(action: :new) if @model.recourse_actions.include? :new %>
|
|
18
|
-
|
|
19
|
-
<%= table items: @resources, cached: @model.recourse_cachable?, positioned: @model.recourse_positionable?, pagy: @pagy, empty_label: "No #{@model.name.underscore.pluralize}" do |resource, section| %>
|
|
20
|
-
<%= render 'row', resource: resource, section: section %>
|
|
21
|
-
<% end %>
|