recourse 7.6.1 → 7.7.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.md +17 -0
- data/config/locales/recourse.en.yml +4 -0
- data/lib/recourse/helpers/choices.rb +7 -7
- data/lib/recourse/helpers/filter_menus.rb +54 -0
- data/lib/recourse/helpers/filters.rb +30 -62
- data/lib/recourse/helpers/reaches.rb +66 -0
- data/lib/recourse/helpers.rb +7 -5
- data/lib/recourse/searchable/filters.rb +2 -2
- data/lib/recourse/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a39f935e577b873d97305e1b599b01b7c36a7e8f7c07b584df0f2449a2a58042
|
|
4
|
+
data.tar.gz: ccce6838f769dcd9b3bc8bdbb00143b17f1a593e39f58567f3054373335f0a6d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16faf5ac22fbc37539dd84b991b58f7027abbcd26bdedb13ecbcd9d877c966844508d8106eb66e99223bbce9e5998fc81ed9df781e03b6f8864ad036abcc8e62
|
|
7
|
+
data.tar.gz: 3155b9735871a0de16dd9e95ea9375bce9ad062d46827afc4549a30c67cf868bd97d2c252e1062ac798b65612c52d7cf4525d52f654b66b82ee1428641dac134
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 7.7.0 - 2026-09-20
|
|
11
|
+
|
|
12
|
+
* [CHANGE] `filter_fields` is a list of predicates, and nothing else
|
|
13
|
+
|
|
14
|
+
A filter was a predicate to the options that drew it — `label:` for its heading,
|
|
15
|
+
`values:` for its words, `scope:` for the rows it offered. All three are gone: a model
|
|
16
|
+
names the filters it wants and the gem works out the rest. A predicate may now reach
|
|
17
|
+
through an association, so `skilled_specialties_id_in` lists the specialties and
|
|
18
|
+
`integration_type_in` the kinds the integrations table holds, each read as the locale
|
|
19
|
+
calls that model.
|
|
20
|
+
|
|
21
|
+
* [FEATURE] A filter Ransack will not answer is refused
|
|
22
|
+
|
|
23
|
+
A predicate whose attribute is missing from `ransackable_attributes` drew a menu that
|
|
24
|
+
narrowed nothing and handed back every row. Ransack is asked first, since Ransack is
|
|
25
|
+
what would drop it, and a model that names one raises rather than drawing it.
|
|
26
|
+
|
|
10
27
|
## 7.6.1 - 2026-09-20
|
|
11
28
|
|
|
12
29
|
* [FIX] The three write seams are marked `@api private`
|
|
@@ -74,6 +74,10 @@ en:
|
|
|
74
74
|
unindexed: "You asked for `recourses %{names}, positionable: true`, which draws no
|
|
75
75
|
index to put in order. A row is dragged from the table that lists it, so there is
|
|
76
76
|
nowhere for a drop to be reported from."
|
|
77
|
+
unanswerable: "%{model} lists `%{predicate}` among its `filter_fields`, and Ransack
|
|
78
|
+
will not answer it: the attribute behind it is missing from
|
|
79
|
+
`ransackable_attributes` or `ransackable_associations`. A menu drawn for it would
|
|
80
|
+
narrow nothing and hand back every row."
|
|
77
81
|
# What a menu with several picks reads as, the first named and the rest counted.
|
|
78
82
|
more: "%{first} + %{count} more"
|
|
79
83
|
new: New %{model}
|
|
@@ -9,10 +9,10 @@ module Recourse
|
|
|
9
9
|
# The values a column admits of itself, where it admits a known set of them:
|
|
10
10
|
# an enum's words, or a boolean's two. Nil for anything else, which is what
|
|
11
11
|
# hands the question on to the key it might be.
|
|
12
|
-
def choice_filter(predicate, column
|
|
13
|
-
return enum_filter predicate, column
|
|
12
|
+
def choice_filter(predicate, column)
|
|
13
|
+
return enum_filter predicate, column if resource_model.defined_enums.key? column
|
|
14
14
|
|
|
15
|
-
boolean_filter predicate, column
|
|
15
|
+
boolean_filter predicate, column if boolean_column? column
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
# The two a boolean admits, read as a reader reads them rather than as the
|
|
@@ -21,8 +21,8 @@ module Recourse
|
|
|
21
21
|
# `Reachable` over a column called `stopped` -- names them with `values:`. The
|
|
22
22
|
# way back is the bare `All`: a column called `signed` pluralizes to nothing
|
|
23
23
|
# anybody would write, so the line says what it does rather than what it is of.
|
|
24
|
-
def boolean_filter(predicate, column
|
|
25
|
-
title =
|
|
24
|
+
def boolean_filter(predicate, column)
|
|
25
|
+
title = resource_model.human_attribute_name column
|
|
26
26
|
values = [[t('recourse.affirmative'), true], [t('recourse.negative'), false]]
|
|
27
27
|
|
|
28
28
|
filter_menu predicate, title, values, t('recourse.all_values')
|
|
@@ -36,8 +36,8 @@ module Recourse
|
|
|
36
36
|
# A menu of the words the column admits, which are the words the form's own menu
|
|
37
37
|
# offers and the badge on a show page reads. Headed by the attribute rather than
|
|
38
38
|
# by a model, since a status is the table's own and not another table's.
|
|
39
|
-
def enum_filter(predicate, column
|
|
40
|
-
title =
|
|
39
|
+
def enum_filter(predicate, column)
|
|
40
|
+
title = resource_model.human_attribute_name column
|
|
41
41
|
values = resource_model.defined_enums[column].keys
|
|
42
42
|
|
|
43
43
|
filter_menu predicate, title, values, choices_all(title)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Recourse
|
|
2
|
+
module Helpers
|
|
3
|
+
# The markup a filter menu is: the records it lists, the order they read in, and
|
|
4
|
+
# the count beside each where the model keeps one.
|
|
5
|
+
module FilterMenus
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def filter_combobox(predicate, title, recourses)
|
|
9
|
+
label = recourses.klass.recourse_label
|
|
10
|
+
counter = filter_counter recourses.klass
|
|
11
|
+
# What it reads as when nothing is ticked, so the way back is a line in the menu.
|
|
12
|
+
models = Recourse.model_title recourses.klass, lower: true
|
|
13
|
+
|
|
14
|
+
filter_menu predicate, title, nil, t('recourse.all', models: models),
|
|
15
|
+
label: label.to_s, counter: counter,
|
|
16
|
+
recourses: filter_options(recourses, label, counter)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The commonest choice first where the model keeps a count, since a menu is read
|
|
20
|
+
# from the top and most requests want the option most rows are behind — and by
|
|
21
|
+
# name where it keeps none. The name breaks ties, or two markets on the same
|
|
22
|
+
# number would swap places between one request and the next.
|
|
23
|
+
def filter_options(recourses, label, counter)
|
|
24
|
+
return recourses.select(:id, label).order label unless counter
|
|
25
|
+
|
|
26
|
+
recourses.select(:id, label, counter).order counter => :desc, label => :asc
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The column on the model a filter lists that counts the rows being filtered —
|
|
30
|
+
# `markets.zips_count` on `/zips`. Read from the counter caches that model keeps
|
|
31
|
+
# rather than from a column named after this one, so a `zips_count` nobody
|
|
32
|
+
# maintains is not a count. None on a nested page: the count is of every row in
|
|
33
|
+
# the table, and the page shows the parent's share of them, which nothing counted.
|
|
34
|
+
def filter_counter(klass)
|
|
35
|
+
return if resource_parent
|
|
36
|
+
|
|
37
|
+
Recourse.counters(klass).find { |_, one| one.klass == resource_model }&.first
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Never invalid and never required: a filter narrows rather than sets.
|
|
41
|
+
def filter_menu(predicate, title, values, all, **)
|
|
42
|
+
render('recourses/combobox', name: "q[#{predicate}]", id: "q_#{predicate}",
|
|
43
|
+
placeholder: title, multiple: true, aria_label: title,
|
|
44
|
+
selected: filter_values(predicate), small: true,
|
|
45
|
+
all: all, values: Array(values), **)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A multiple select submits one value per pick, and a link carries them the same way.
|
|
49
|
+
def filter_values(predicate)
|
|
50
|
+
Array(query_params[predicate]).map(&:to_s).compact_blank
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -10,7 +10,7 @@ module Recourse
|
|
|
10
10
|
# rather than picked — so what is left is what a form would hold, which is
|
|
11
11
|
# what decides whether there is a form at all.
|
|
12
12
|
def resource_filter_fields
|
|
13
|
-
resource_filters.filter_map { |predicate
|
|
13
|
+
resource_filters.filter_map { |predicate| filter_field predicate }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# The model's filters, less the one a nested route already answered:
|
|
@@ -18,80 +18,48 @@ module Recourse
|
|
|
18
18
|
# for it would only offer to re-ask — or to contradict — the address.
|
|
19
19
|
def resource_filters
|
|
20
20
|
parent = resource_parent_association
|
|
21
|
-
filters =
|
|
21
|
+
filters = declared_filters
|
|
22
22
|
return filters unless parent
|
|
23
23
|
|
|
24
|
-
filters
|
|
24
|
+
filters - ["#{parent.foreign_key}_in"]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
# The predicates the model named, as words. A filter is named and nothing else:
|
|
28
|
+
# what it reads as and what it offers are the column's to say.
|
|
29
|
+
def declared_filters
|
|
30
|
+
resource_model.filter_fields.map(&:to_s)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# One filter: the values a column of its own admits, a menu of the records a
|
|
34
|
+
# foreign key points at, or one reached through an association. A predicate
|
|
35
|
+
# Ransack will not answer is refused here rather than drawn, a menu that
|
|
36
|
+
# narrows nothing being worse than no menu at all.
|
|
37
|
+
def filter_field(predicate)
|
|
38
|
+
refuse_unanswerable predicate
|
|
39
|
+
column = predicate.sub Search::LIST_PREDICATES, ''
|
|
33
40
|
|
|
34
|
-
column
|
|
41
|
+
choice_filter(predicate, column) || reference_filter(predicate, column) ||
|
|
42
|
+
reached_filter(predicate, column)
|
|
43
|
+
end
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
# Ransack decides, since Ransack is what would drop it: a model answers for the
|
|
46
|
+
# attributes it allows and no others, and a filter naming one it does not allow
|
|
47
|
+
# comes back holding every row.
|
|
48
|
+
def refuse_unanswerable(predicate)
|
|
49
|
+
resource_model.ransack({ predicate => ['1'] }, ignore_unknown_conditions: false)
|
|
50
|
+
rescue ::Ransack::InvalidSearchError
|
|
51
|
+
raise Error, I18n.t('recourse.unanswerable', model: resource_model, predicate:)
|
|
37
52
|
end
|
|
38
53
|
|
|
39
54
|
# A menu of the records a foreign key points at. Nothing where that key is typed
|
|
40
55
|
# rather than picked, which is the same question the field beside it asks: the
|
|
41
56
|
# label is bounded, or the table is too long to list. Either way the menu would
|
|
42
|
-
# be a table of its own.
|
|
43
|
-
def reference_filter(predicate, column
|
|
57
|
+
# be a table of its own.
|
|
58
|
+
def reference_filter(predicate, column)
|
|
44
59
|
association = belongs_to_association column
|
|
45
|
-
return if association.nil? ||
|
|
46
|
-
(scope.nil? && association.klass.recourse_typed_reference?)
|
|
47
|
-
|
|
48
|
-
filter_combobox predicate, label || reference_title(column, association),
|
|
49
|
-
(scope || association.klass).all
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def filter_combobox(predicate, title, recourses)
|
|
53
|
-
label = recourses.klass.recourse_label
|
|
54
|
-
counter = filter_counter recourses.klass
|
|
55
|
-
# What it reads as when nothing is ticked, so the way back is a line in the menu.
|
|
56
|
-
models = Recourse.model_title recourses.klass, lower: true
|
|
57
|
-
|
|
58
|
-
filter_menu predicate, title, nil, t('recourse.all', models: models),
|
|
59
|
-
label: label.to_s, counter: counter,
|
|
60
|
-
recourses: filter_options(recourses, label, counter)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# The commonest choice first where the model keeps a count, since a menu is read
|
|
64
|
-
# from the top and most requests want the option most rows are behind — and by
|
|
65
|
-
# name where it keeps none. The name breaks ties, or two markets on the same
|
|
66
|
-
# number would swap places between one request and the next.
|
|
67
|
-
def filter_options(recourses, label, counter)
|
|
68
|
-
return recourses.select(:id, label).order label unless counter
|
|
69
|
-
|
|
70
|
-
recourses.select(:id, label, counter).order counter => :desc, label => :asc
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# The column on the model a filter lists that counts the rows being filtered —
|
|
74
|
-
# `markets.zips_count` on `/zips`. Read from the counter caches that model keeps
|
|
75
|
-
# rather than from a column named after this one, so a `zips_count` nobody
|
|
76
|
-
# maintains is not a count. None on a nested page: the count is of every row in
|
|
77
|
-
# the table, and the page shows the parent's share of them, which nothing counted.
|
|
78
|
-
def filter_counter(klass)
|
|
79
|
-
return if resource_parent
|
|
80
|
-
|
|
81
|
-
Recourse.counters(klass).find { |_, one| one.klass == resource_model }&.first
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Never invalid and never required: a filter narrows rather than sets.
|
|
85
|
-
def filter_menu(predicate, title, values, all, **)
|
|
86
|
-
render('recourses/combobox', name: "q[#{predicate}]", id: "q_#{predicate}",
|
|
87
|
-
placeholder: title, multiple: true, aria_label: title,
|
|
88
|
-
selected: filter_values(predicate), small: true,
|
|
89
|
-
all: all, values: Array(values), **)
|
|
90
|
-
end
|
|
60
|
+
return if association.nil? || association.klass.recourse_typed_reference?
|
|
91
61
|
|
|
92
|
-
|
|
93
|
-
def filter_values(predicate)
|
|
94
|
-
Array(query_params[predicate]).map(&:to_s).compact_blank
|
|
62
|
+
filter_combobox predicate, reference_title(column, association), association.klass.all
|
|
95
63
|
end
|
|
96
64
|
end
|
|
97
65
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Recourse
|
|
2
|
+
module Helpers
|
|
3
|
+
# A filter that reaches through an association: `skilled_specialties_id_in` on a
|
|
4
|
+
# provider names the specialties it is skilled in, and `integration_type_in` the
|
|
5
|
+
# CRM its integration is. The predicate says which association and which of its
|
|
6
|
+
# columns, and the column says what the menu is of.
|
|
7
|
+
module Reaches
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# A menu for a column on another model, where the predicate names the way there.
|
|
11
|
+
# The rows a key points at, or the kinds a table holds where the column is the
|
|
12
|
+
# one Rails keeps a subclass in, or whatever else that column holds.
|
|
13
|
+
def reached_filter(predicate, column)
|
|
14
|
+
association, attribute = reached_association column
|
|
15
|
+
return unless association
|
|
16
|
+
|
|
17
|
+
klass = association.klass
|
|
18
|
+
title = klass.model_name.human
|
|
19
|
+
return rows_filter predicate, title, klass if attribute == klass.primary_key
|
|
20
|
+
|
|
21
|
+
held_filter predicate, title, klass, attribute
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Every row of the other table, unless it is too long to be a menu — the same
|
|
25
|
+
# question a key pointing at it answers, since a menu of forty thousand buttons
|
|
26
|
+
# is a table wearing the wrong clothes.
|
|
27
|
+
def rows_filter(predicate, title, klass)
|
|
28
|
+
filter_combobox predicate, title, klass.all unless klass.recourse_typed_reference?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The association a column reaches through, and what is left of the column once
|
|
32
|
+
# its name is taken off. The longest name wins, so `listed_contact_id` reaches
|
|
33
|
+
# through `listed_contact` rather than through a `listed` beside it.
|
|
34
|
+
def reached_association(column)
|
|
35
|
+
named = resource_model.reflect_on_all_associations.select do |one|
|
|
36
|
+
column.start_with? "#{one.name}_"
|
|
37
|
+
end
|
|
38
|
+
one = named.max_by { |association| association.name.length }
|
|
39
|
+
|
|
40
|
+
[one, column.delete_prefix("#{one.name}_")] if one
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The values a column on the other table holds, read from the rows rather than
|
|
44
|
+
# from anywhere they are declared: a value nobody has written is a filter that
|
|
45
|
+
# would answer nothing, and a column holding more of them than a menu takes is a
|
|
46
|
+
# table wearing the wrong clothes.
|
|
47
|
+
def held_filter(predicate, title, klass, attribute)
|
|
48
|
+
return unless klass.column_names.include? attribute
|
|
49
|
+
|
|
50
|
+
held = klass.distinct.limit(Searchable::MENU_LIMIT + 1).pluck(attribute).compact.sort
|
|
51
|
+
return if held.empty? || held.size > Searchable::MENU_LIMIT
|
|
52
|
+
|
|
53
|
+
values_filter(predicate, title, held.map { |one| [held_title(klass, attribute, one), one] })
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# What one of them reads as: the word itself, unless the column is the one Rails
|
|
57
|
+
# keeps a subclass in, where it reads as the locale calls that model — the key
|
|
58
|
+
# Rails writes a subclass under, so no class is named to find it.
|
|
59
|
+
def held_title(klass, attribute, value)
|
|
60
|
+
return value unless attribute == klass.inheritance_column
|
|
61
|
+
|
|
62
|
+
t "activerecord.models.#{value.underscore}", default: value.demodulize.titleize
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/recourse/helpers.rb
CHANGED
|
@@ -19,6 +19,7 @@ require_relative 'helpers/details'
|
|
|
19
19
|
require_relative 'helpers/events'
|
|
20
20
|
require_relative 'helpers/examples'
|
|
21
21
|
require_relative 'helpers/fields'
|
|
22
|
+
require_relative 'helpers/filter_menus'
|
|
22
23
|
require_relative 'helpers/filters'
|
|
23
24
|
require_relative 'helpers/flashes'
|
|
24
25
|
require_relative 'helpers/formats'
|
|
@@ -32,6 +33,7 @@ require_relative 'helpers/navigation'
|
|
|
32
33
|
require_relative 'helpers/parents'
|
|
33
34
|
require_relative 'helpers/pictures'
|
|
34
35
|
require_relative 'helpers/previews'
|
|
36
|
+
require_relative 'helpers/reaches'
|
|
35
37
|
require_relative 'helpers/references'
|
|
36
38
|
require_relative 'helpers/resources'
|
|
37
39
|
require_relative 'helpers/refreshes'
|
|
@@ -56,11 +58,11 @@ module Recourse
|
|
|
56
58
|
module Helpers
|
|
57
59
|
include Actions, Positions, Attachments, Blobs, Bookmarks, Breadcrumbs, Buttons,
|
|
58
60
|
Calendars, Cards, Cells, Choices, Colors, Comboboxes, Constraints, Counters,
|
|
59
|
-
Deletions, Densities, Details, Events, Examples, Fields,
|
|
60
|
-
Formats, Inputs, Kinds, Limits, Links, Maps, Names, Navigation,
|
|
61
|
-
Pictures, Previews, References, Refreshes, Routing,
|
|
62
|
-
Shapes, Shortcuts, Sidebars, Sorts, Spans, Tabs,
|
|
63
|
-
Warnings, Weeks, Zones
|
|
61
|
+
Deletions, Densities, Details, Events, Examples, Fields, FilterMenus, Filters,
|
|
62
|
+
Flashes, Formats, Inputs, Kinds, Limits, Links, Maps, Names, Navigation,
|
|
63
|
+
Parents, Pictures, Previews, Reaches, References, Refreshes, Routing,
|
|
64
|
+
Resources, Rows, Searches, Shapes, Shortcuts, Sidebars, Sorts, Spans, Tabs,
|
|
65
|
+
Themes, Times, Values, Warnings, Weeks, Zones
|
|
64
66
|
|
|
65
67
|
# The grid a record's own two pages lay an attribute out in: two columns on a large
|
|
66
68
|
# viewport, and the same padding on both, so a value and the field that edits it sit
|
|
@@ -11,7 +11,7 @@ module Recourse
|
|
|
11
11
|
# big to list. The model's own columns come first, before the menus that name
|
|
12
12
|
# other tables.
|
|
13
13
|
def filter_fields
|
|
14
|
-
enum_filter_fields
|
|
14
|
+
enum_filter_fields + boolean_filter_fields + reference_filter_fields
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
private
|
|
@@ -27,7 +27,7 @@ module Recourse
|
|
|
27
27
|
# Every one of the three comes to the same shape, and `_in` is what lets a
|
|
28
28
|
# request tick more than one of the values a menu offers.
|
|
29
29
|
def filters_for(names)
|
|
30
|
-
names.
|
|
30
|
+
names.map { |name| "#{name}_in" }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# One per enum: a dozen words a column admits are a menu whatever else is on the
|
data/lib/recourse/version.rb
CHANGED
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.
|
|
4
|
+
version: 7.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- claudiob
|
|
@@ -211,6 +211,7 @@ files:
|
|
|
211
211
|
- lib/recourse/helpers/events.rb
|
|
212
212
|
- lib/recourse/helpers/examples.rb
|
|
213
213
|
- lib/recourse/helpers/fields.rb
|
|
214
|
+
- lib/recourse/helpers/filter_menus.rb
|
|
214
215
|
- lib/recourse/helpers/filters.rb
|
|
215
216
|
- lib/recourse/helpers/flashes.rb
|
|
216
217
|
- lib/recourse/helpers/formats.rb
|
|
@@ -225,6 +226,7 @@ files:
|
|
|
225
226
|
- lib/recourse/helpers/pictures.rb
|
|
226
227
|
- lib/recourse/helpers/positions.rb
|
|
227
228
|
- lib/recourse/helpers/previews.rb
|
|
229
|
+
- lib/recourse/helpers/reaches.rb
|
|
228
230
|
- lib/recourse/helpers/references.rb
|
|
229
231
|
- lib/recourse/helpers/refreshes.rb
|
|
230
232
|
- lib/recourse/helpers/resources.rb
|