recourse 7.7.1 → 7.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7701d1c2564537b038401ab7224c1adebed9e766a9c3c316090e7b931a8eea2e
4
- data.tar.gz: 72c419b28ec5ec1e6ed9704b753b36663daee1dfbc91a9d84b96dce934548f23
3
+ metadata.gz: db928d015c6b427908eac64f93319534dafb40dd089916ab38782241ba6264dd
4
+ data.tar.gz: 68abc102048e76b5440c010cba4f039e2c5388496c258bc8cc73c23adadfb940
5
5
  SHA512:
6
- metadata.gz: aadd0f9de7e9f02148030d1eae72c31eef0e5a4b74108ccae7243366d749e1d251521ff91aa78db616313725cb9bd90cd1f83e7ddc18c4544b6ee62bbed90482
7
- data.tar.gz: a00d680bd48d7c9fa32aaf17d4feee6327853b8c267f3497d8c2372d986fb646010278cb1ab1c7648512713383822c2c78a80f588760bee3a49035b037e46ef7
6
+ metadata.gz: ce01d852728a901751c26b717fa7008143a36b96038cf9e672f121a38c3929d83bd24b4e45575bbe13439362a850a11a513288e4bed372678e2d4385071c833d
7
+ data.tar.gz: 798c8ccef55728658d9ec54b7a5076b80c6f83888e8c6728ba53958445ee0b4c12c599744c88134650c3ec4989fb690dd4480ecf280b2f9751cefd14c20c0e92
data/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## 7.8.1 - 2026-09-20
11
+
12
+ * [FIX] A retrieval route is named after its resource
13
+
14
+ A collection route takes its name from the scope around it, so four retrievable
15
+ resources under one parent all answered to `provider_retrieval` and only the first
16
+ kept the name. Each is `provider_visits_retrieval` now, and the other three have a
17
+ path helper again.
18
+
19
+ ## 7.8.0 - 2026-09-20
20
+
21
+ * [FEATURE] A resource may say its rows can be fetched again
22
+
23
+ `recourses :visits, only: :index, retrievable: true` draws `POST /providers/5/visits/retrieval`
24
+ and puts the button that asks for it on the table that lists them. Where the rows came
25
+ from is the host's to know, so the controller is the host's to write and the gem makes
26
+ one only where there is none.
27
+
28
+ A page says when there is nothing to fetch from: `recourse_retrievable?` answers true
29
+ unless a host narrows it, which is how an index of a CRM's work offers nothing to a
30
+ provider who has connected none. And a resource asking for it without an index is
31
+ refused while the routes draw, the button having nowhere to stand.
32
+
10
33
  ## 7.7.1 - 2026-09-20
11
34
 
12
35
  * [FIX] The filters the gem offers itself are ones Ransack will answer
@@ -0,0 +1,16 @@
1
+ module Recourse
2
+ # Whether a page offers to fetch its rows again from wherever they came.
3
+ module Retrieving
4
+ extend ActiveSupport::Concern
5
+
6
+ included { helper_method :recourse_retrievable? }
7
+
8
+ private
9
+
10
+ # Whether this page offers to fetch its rows again, where the routes said it may.
11
+ # True unless the host says otherwise, since a provider with no CRM connected has
12
+ # nothing to fetch from and only the host knows it: `def recourse_retrievable? =
13
+ # @provider.integrated?`. Private, so narrowing it adds no action.
14
+ def recourse_retrievable? = true
15
+ end
16
+ end
@@ -5,7 +5,7 @@ module Recourse
5
5
  class BaseController < ApplicationController
6
6
  include Pagy::Method, Positioned, AttachmentResolution, AttachmentWriting,
7
7
  Landing, Paging, ListResolution, ParameterResolution, ParentNaming,
8
- ParentResolution, ReferenceResolution, ResourceResolution,
8
+ ParentResolution, ReferenceResolution, ResourceResolution, Retrieving,
9
9
  Weeks, Zoning
10
10
 
11
11
  helper Helpers
@@ -12,6 +12,8 @@
12
12
  <% end %>
13
13
  <% end %>
14
14
 
15
+ <% content_for :actions, retrieval_button %>
16
+
15
17
  <% content_for :search do %>
16
18
  <%= search_form %>
17
19
  <% end %>
@@ -71,6 +71,11 @@ en:
71
71
  has no %{model} model.
72
72
  nested: You nested `recourses %{names}` inside `resources :%{parent}`, which does
73
73
  not namespace what it holds. Nest it under `recourses :%{parent}` instead.
74
+ # What the button that fetches a table's rows again is named to a screen reader.
75
+ retrieve: Retrieve %{models} again
76
+ unretrieved: "You asked for `recourses %{names}, retrievable: true`, which draws no
77
+ index to fetch them onto. The button that asks for them stands on the table that
78
+ lists them, so there is nowhere to put it."
74
79
  unindexed: "You asked for `recourses %{names}, positionable: true`, which draws no
75
80
  index to put in order. A row is dragged from the table that lists it, so there is
76
81
  nowhere for a drop to be reported from."
@@ -0,0 +1,31 @@
1
+ module Recourse
2
+ module Helpers
3
+ # The button that fetches a table's rows again from wherever they came.
4
+ module Retrievals
5
+ private
6
+
7
+ # Offered where the routes said the rows may be fetched again and the page says
8
+ # there is anything to fetch them from — an index of a CRM's work is no use to a
9
+ # provider who has connected none, and only the host knows that.
10
+ def retrieval_button
11
+ return unless Recourse.retrievable?(controller.controller_path) && recourse_retrievable?
12
+
13
+ button_to retrieval_path, class: 'btn btn-sm btn-solid theme-primary ms-3',
14
+ form_class: 'd-inline-block',
15
+ aria: { label: retrieval_label } do
16
+ icon_tag :sync
17
+ end
18
+ end
19
+
20
+ # The one route a retrievable resource earns, under the resource itself.
21
+ def retrieval_path
22
+ url_for controller: "/#{controller.controller_path}/retrievals", action: :create
23
+ end
24
+
25
+ def retrieval_label
26
+ t 'recourse.retrieve', models: Recourse.model_title(resource_model,
27
+ lower: true)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -35,6 +35,7 @@ require_relative 'helpers/pictures'
35
35
  require_relative 'helpers/previews'
36
36
  require_relative 'helpers/reaches'
37
37
  require_relative 'helpers/references'
38
+ require_relative 'helpers/retrievals'
38
39
  require_relative 'helpers/resources'
39
40
  require_relative 'helpers/refreshes'
40
41
  require_relative 'helpers/routing'
@@ -61,7 +62,8 @@ module Recourse
61
62
  Deletions, Densities, Details, Events, Examples, Fields, FilterMenus, Filters,
62
63
  Flashes, Formats, Inputs, Kinds, Limits, Links, Maps, Names, Navigation,
63
64
  Parents, Pictures, Previews, Reaches, References, Refreshes, Routing,
64
- Resources, Rows, Searches, Shapes, Shortcuts, Sidebars, Sorts, Spans, Tabs,
65
+ Resources, Retrievals, Rows, Searches, Shapes, Shortcuts, Sidebars, Sorts, Spans,
66
+ Tabs,
65
67
  Themes, Times, Values, Warnings, Weeks, Zones
66
68
 
67
69
  # The grid a record's own two pages lay an attribute out in: two columns on a large
@@ -19,6 +19,14 @@ module Recourse
19
19
  @parents[child.to_s] = parent.to_s
20
20
  end
21
21
 
22
+ # Records that a resource's rows may be fetched again from wherever they came.
23
+ def retrieve(path)
24
+ @retrievable << path.to_s unless @retrievable.include? path.to_s
25
+ end
26
+
27
+ # Whether that resource's index offers to fetch them.
28
+ def retrievable?(path) = @retrievable.include?(path.to_s)
29
+
22
30
  # The resources nested under one parent path, in the order they were drawn.
23
31
  def nested_under(parent)
24
32
  @nested.fetch parent.to_s, []
@@ -8,12 +8,13 @@ module Recourse
8
8
  # What a resource holds: the square that keeps one of its rows, the place a row
9
9
  # holds in a table somebody positioned, and whatever the host's own block declared —
10
10
  # each under the resource's own module, which is what every nested page relies on.
11
- def draw_within(keepable, positionable, block)
11
+ def draw_within(keepable, positionable, retrievable, block)
12
12
  addressable = addressable_rows?
13
13
 
14
14
  scope module: parent_resource.name do
15
15
  draw_bookmark if keepable && addressable
16
16
  draw_position if positionable && addressable
17
+ draw_retrieval if retrievable
17
18
  instance_exec(&block) if block
18
19
  end
19
20
  end
@@ -37,6 +38,21 @@ module Recourse
37
38
  resource :position, only: :update
38
39
  end
39
40
 
41
+ # The fetch a table's own button asks for, at `/providers/5/visits/retrieval`.
42
+ # Where the rows came from is the host's to know, so the controller is the
43
+ # host's to write and the gem only makes one where there is none — the same
44
+ # bargain a bare action strikes. Recorded nowhere, for the reason the bookmark
45
+ # gives: a tab and a bare-action button both look under a resource, and this is
46
+ # neither.
47
+ def draw_retrieval
48
+ Controllers.define_missing [current_module, 'retrievals'].compact.join('/')
49
+ # Named after the resource as well as the parent: a collection route takes its
50
+ # name from the scope around it, so four retrievals under one parent would all
51
+ # answer to `provider_retrieval` and only the first would keep the name.
52
+ named = "#{parent_resource.name}_retrieval"
53
+ collection { resource :retrieval, only: :create, as: named }
54
+ end
55
+
40
56
  # The row a table's bookmark square writes: one record kept by whoever is looking,
41
57
  # at `/places/5/bookmark`. Deliberately not recorded through `Recourse.nest` — it
42
58
  # would sit directly under the resource, where a tab and a bare-action button both
@@ -0,0 +1,34 @@
1
+ module Recourse
2
+ module Routes
3
+ # What the routes file is not allowed to say, said back to whoever wrote it while
4
+ # the routes draw rather than answered with a broken page later.
5
+ module Refusals
6
+ private
7
+
8
+ # The two that mean nothing without a page listing rows: a row is dragged from the
9
+ # table that lists it, and a button fetching them again stands on that same table.
10
+ # The retrieval is recorded here too, where a helper reads it back.
11
+ def refuse_unindexed(names, positioned, fetched, options)
12
+ refuse_unlisted names, 'unindexed', options if positioned
13
+ refuse_unlisted names, 'unretrieved', options if fetched
14
+ names.each { |name| Recourse.retrieve declared_path(name) } if fetched
15
+
16
+ options
17
+ end
18
+
19
+ def refuse_unlisted(names, word, options)
20
+ return if indexed? options
21
+
22
+ raise Error, I18n.t("recourse.#{word}", names: names.map(&:inspect).join(', '))
23
+ end
24
+
25
+ def refuse_unscoped_nesting(names)
26
+ parent = parent_resource
27
+ return if parent.nil? || current_module.to_s.split('/').include?(parent.name)
28
+
29
+ raise Error, I18n.t('recourse.nested', names: names.map(&:inspect).join(', '),
30
+ parent: parent.name)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,9 +1,10 @@
1
1
  require_relative 'routes/nested'
2
+ require_relative 'routes/refusals'
2
3
 
3
4
  module Recourse
4
5
  # Extends the config/routes.rb DSL, so `recourses` works anywhere `resources` does.
5
6
  module Routes
6
- include Nested
7
+ include Nested, Refusals
7
8
 
8
9
  # Draws what `resources` draws, after supplying any controller the host lacks. A
9
10
  # block nests what it declares under each resource — ZIPs at
@@ -26,13 +27,12 @@ module Recourse
26
27
  names.each { |name| declare_resource name }
27
28
  # The host's word, and taken out before Rails sees the rest: `resources` would
28
29
  # refuse a keyword it does not know.
29
- positionable = options.delete(:positionable) { false }
30
- options = default_nested_actions options
31
- refuse_unindexed_positioning names, positionable, options
30
+ positioned = options.delete(:positionable) { false }
31
+ fetched = options.delete(:retrievable) { false }
32
+ options = refuse_unindexed names, positioned, fetched, default_nested_actions(options)
33
+ return resources(*names, **options) unless [keepable, positioned, fetched, block].any?
32
34
 
33
- return resources(*names, **options) unless block || keepable || positionable
34
-
35
- resources(*names, **options) { draw_within keepable, positionable, block }
35
+ resources(*names, **options) { draw_within keepable, positioned, fetched, block }
36
36
  end
37
37
 
38
38
  # What `resource` draws, recorded the same way: one record reached without an id,
@@ -57,18 +57,16 @@ module Recourse
57
57
  # The module is the namespace being drawn in, so a resource is declared and its
58
58
  # controller defined under the path Rails will route to.
59
59
  def declare_resource(name)
60
- path = [current_module, name].compact.join '/'
61
- record_declaration path
62
- Controllers.define_missing path
60
+ record_declaration declared_path(name)
61
+ Controllers.define_missing declared_path(name)
63
62
  end
64
63
 
64
+ # Where a resource of this name is routed, which is the name under everything the
65
+ # routes file remembers about it.
66
+ def declared_path(name) = [current_module, name].compact.join('/')
67
+
65
68
  # A table is put in order from its index and from nowhere else, so asking for the
66
69
  # route without drawing one is a host saying two things that cannot both hold.
67
- def refuse_unindexed_positioning(names, positionable, options)
68
- return unless positionable && !indexed?(options)
69
-
70
- raise Error, I18n.t('recourse.unindexed', names: names.map(&:inspect).join(', '))
71
- end
72
70
 
73
71
  # Whether `index` survived the `only:` or `except:` the host wrote. Neither of
74
72
  # them is the common case, and both name the action plainly.
@@ -84,13 +82,5 @@ module Recourse
84
82
 
85
83
  options.merge only: %i[index new create]
86
84
  end
87
-
88
- def refuse_unscoped_nesting(names)
89
- parent = parent_resource
90
- return if parent.nil? || current_module.to_s.split('/').include?(parent.name)
91
-
92
- raise Error, I18n.t('recourse.nested', names: names.map(&:inspect).join(', '),
93
- parent: parent.name)
94
- end
95
85
  end
96
86
  end
@@ -1,4 +1,4 @@
1
1
  module Recourse
2
2
  # Version of the gem, read by the gemspec and by hosts checking compatibility.
3
- VERSION = '7.7.1'
3
+ VERSION = '7.8.1'
4
4
  end
data/lib/recourse.rb CHANGED
@@ -54,6 +54,7 @@ module Recourse
54
54
  end
55
55
 
56
56
  @declared = []
57
+ @retrievable = []
57
58
  @nested = {}
58
59
  @parents = {}
59
60
  @declared_bookmarks = nil
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: 7.7.1
4
+ version: 7.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob
@@ -146,6 +146,7 @@ files:
146
146
  - app/controllers/concerns/recourse/positioned.rb
147
147
  - app/controllers/concerns/recourse/reference_resolution.rb
148
148
  - app/controllers/concerns/recourse/resource_resolution.rb
149
+ - app/controllers/concerns/recourse/retrieving.rb
149
150
  - app/controllers/concerns/recourse/weeks.rb
150
151
  - app/controllers/concerns/recourse/zoning.rb
151
152
  - app/controllers/recourse/base_controller.rb
@@ -230,6 +231,7 @@ files:
230
231
  - lib/recourse/helpers/references.rb
231
232
  - lib/recourse/helpers/refreshes.rb
232
233
  - lib/recourse/helpers/resources.rb
234
+ - lib/recourse/helpers/retrievals.rb
233
235
  - lib/recourse/helpers/routing.rb
234
236
  - lib/recourse/helpers/rows.rb
235
237
  - lib/recourse/helpers/searches.rb
@@ -256,6 +258,7 @@ files:
256
258
  - lib/recourse/registry.rb
257
259
  - lib/recourse/routes.rb
258
260
  - lib/recourse/routes/nested.rb
261
+ - lib/recourse/routes/refusals.rb
259
262
  - lib/recourse/routing.rb
260
263
  - lib/recourse/scopes.rb
261
264
  - lib/recourse/search.rb