terrazzo 0.7.3 → 0.7.4
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 +11 -0
- data/app/controllers/terrazzo/application_controller.rb +5 -2
- data/lib/terrazzo/search.rb +45 -29
- data/lib/terrazzo/version.rb +1 -1
- data/terrazzo.gemspec +3 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 69d154e835cecd712223cce10063d1e51a185dbe26c705acab6b9f040d4dd0b8
|
|
4
|
+
data.tar.gz: ef0f793e70ca95e739eefc40b64b3188ae1774fb3338489316136fc19b08f746
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11217eadfa4391055dec08ea2958cabdc10954ac653b027a14130e632ff0c7e5643657ca486c0096909014ec9276ea80909036e3aa267408d71604839f0bc9cd
|
|
7
|
+
data.tar.gz: ad45007e3d20d3d32e26939e46cf58ad62245c240e49ab9f2e0d9293252f18dcdde30e6873237cbc5b31ce90a0c85fb0442cfcbb8fc73c151a86699975e50c28
|
data/README.md
CHANGED
|
@@ -63,6 +63,9 @@ Full docs at **[gohypelab.github.io/terrazzo](https://gohypelab.github.io/terraz
|
|
|
63
63
|
- Node.js 18+
|
|
64
64
|
- A JS bundler (Vite recommended, esbuild also supported)
|
|
65
65
|
|
|
66
|
+
Terrazzo selects JSON 2.x because current Rails releases do not support the
|
|
67
|
+
JSON 3 argument format. See [the Rails compatibility fix](https://github.com/rails/rails/pull/58601).
|
|
68
|
+
|
|
66
69
|
## Development and releases
|
|
67
70
|
|
|
68
71
|
Pull requests and pushes to `main` run the Ruby unit specs, npm package checks,
|
|
@@ -85,6 +88,14 @@ retried safely.
|
|
|
85
88
|
See [RELEASING.md](RELEASING.md) for trusted publisher setup, release commands,
|
|
86
89
|
and retry instructions.
|
|
87
90
|
|
|
91
|
+
## Customizing search
|
|
92
|
+
|
|
93
|
+
Enable search with `searchable: true` in a dashboard field's options. Search supports
|
|
94
|
+
numeric columns, JSON store accessors, and associated records. Override the
|
|
95
|
+
controller's `search_resources` method to add custom search syntax.
|
|
96
|
+
See [search fields](docs/customizing-dashboards.md#search-fields) and
|
|
97
|
+
[custom search](docs/customizing-controller-actions.md#customizing-search).
|
|
98
|
+
|
|
88
99
|
## Customizing Per-Row Actions
|
|
89
100
|
|
|
90
101
|
Terrazzo generates Show, Edit, and Destroy action buttons for each row on index pages and has_many tables on show pages.
|
|
@@ -158,9 +158,12 @@ module Terrazzo
|
|
|
158
158
|
resource_class.all
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
+
def search_resources
|
|
162
|
+
Terrazzo::Search.new(scoped_resource, dashboard, params[:search]).run
|
|
163
|
+
end
|
|
164
|
+
|
|
161
165
|
def index_resources_and_order
|
|
162
|
-
|
|
163
|
-
resources = search.run
|
|
166
|
+
resources = search_resources
|
|
164
167
|
|
|
165
168
|
filter = Terrazzo::Filter.new(resources, dashboard, params[:filter], params[:filter_value])
|
|
166
169
|
resources = filter.run
|
data/lib/terrazzo/search.rb
CHANGED
|
@@ -9,11 +9,7 @@ module Terrazzo
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def run
|
|
12
|
-
|
|
13
|
-
scoped_resource
|
|
14
|
-
else
|
|
15
|
-
search_results
|
|
16
|
-
end
|
|
12
|
+
term.blank? ? scoped_resource : search_results
|
|
17
13
|
end
|
|
18
14
|
|
|
19
15
|
private
|
|
@@ -21,52 +17,72 @@ module Terrazzo
|
|
|
21
17
|
LIKE_ESCAPE = "\\"
|
|
22
18
|
|
|
23
19
|
def search_results
|
|
24
|
-
@association_search_joined = false
|
|
25
20
|
searchable_attributes = dashboard.search_attributes
|
|
26
21
|
return scoped_resource if searchable_attributes.empty?
|
|
27
22
|
|
|
28
|
-
conditions =
|
|
29
|
-
|
|
23
|
+
conditions =
|
|
24
|
+
searchable_attributes
|
|
25
|
+
.map do |attr|
|
|
26
|
+
type = dashboard.attribute_type_for(attr)
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
if type.respond_to?(:associative?) && type.associative?
|
|
29
|
+
build_association_search(attr, type)
|
|
30
|
+
else
|
|
31
|
+
text_attribute(scoped_resource.model, attr).matches(search_pattern, LIKE_ESCAPE)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
.compact
|
|
38
35
|
|
|
39
36
|
return scoped_resource if conditions.empty?
|
|
40
37
|
|
|
41
38
|
combined = conditions.reduce(:or)
|
|
42
|
-
|
|
43
|
-
@association_search_joined ? results.distinct : results
|
|
39
|
+
scoped_resource.where(combined)
|
|
44
40
|
end
|
|
45
41
|
|
|
46
42
|
def build_association_search(attr, type)
|
|
47
43
|
reflection = scoped_resource.model.reflect_on_association(attr)
|
|
48
44
|
return nil unless reflection
|
|
49
45
|
|
|
50
|
-
assoc_table = reflection.klass.arel_table
|
|
51
46
|
columns = association_search_columns(type, reflection.klass)
|
|
52
47
|
return nil if columns.empty?
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
condition =
|
|
50
|
+
columns
|
|
51
|
+
.map { |column| text_attribute(reflection.klass, column).matches(search_pattern, LIKE_ESCAPE) }
|
|
52
|
+
.reduce(:or)
|
|
53
|
+
|
|
54
|
+
# Match IDs so association joins do not duplicate rows or compare JSON columns.
|
|
55
|
+
model = scoped_resource.model
|
|
56
|
+
primary_key = model.arel_table[model.primary_key]
|
|
57
|
+
ids = model.unscoped.joins(attr).where(condition).reselect(primary_key)
|
|
58
|
+
primary_key.in(ids.arel)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def association_search_columns(type, associated_class)
|
|
62
62
|
configured = type.respond_to?(:options) ? Array(type.options[:searchable_fields]) : []
|
|
63
|
-
candidates = configured.presence || [
|
|
64
|
-
column_names = associated_class.column_names
|
|
63
|
+
candidates = configured.presence || %i[name title email]
|
|
64
|
+
column_names = associated_class.column_names + associated_class.stored_attributes.values.flatten.map(&:to_s)
|
|
65
65
|
|
|
66
|
-
candidates
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
candidates.map(&:to_s).select { |column| column_names.include?(column) }.map(&:to_sym)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def text_attribute(model, attribute)
|
|
70
|
+
mysql = %w[Mysql2 Trilogy].include?(model.connection.adapter_name)
|
|
71
|
+
store = model.stored_attributes.find { |_column, fields| fields.include?(attribute.to_sym) }
|
|
72
|
+
expression =
|
|
73
|
+
if store
|
|
74
|
+
column = model.arel_table[store.first]
|
|
75
|
+
if mysql
|
|
76
|
+
path = Arel::Nodes.build_quoted("$.#{attribute.to_s.to_json}")
|
|
77
|
+
value = Arel::Nodes::NamedFunction.new("JSON_EXTRACT", [column, path])
|
|
78
|
+
Arel::Nodes::NamedFunction.new("JSON_UNQUOTE", [value])
|
|
79
|
+
else
|
|
80
|
+
Arel::Nodes::InfixOperation.new("->>", column, Arel::Nodes.build_quoted(attribute.to_s))
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
model.arel_table[attribute]
|
|
84
|
+
end
|
|
85
|
+
model.arel_table.cast(expression, mysql ? "CHAR" : "text")
|
|
70
86
|
end
|
|
71
87
|
|
|
72
88
|
def search_pattern
|
data/lib/terrazzo/version.rb
CHANGED
data/terrazzo.gemspec
CHANGED
|
@@ -31,6 +31,9 @@ Gem::Specification.new do |spec|
|
|
|
31
31
|
spec.require_paths = ["lib"]
|
|
32
32
|
|
|
33
33
|
spec.add_dependency "rails", ">= 7.1"
|
|
34
|
+
# Current Rails releases require the JSON 2 argument format.
|
|
35
|
+
# Remove this limit after released Rails versions include rails/rails#58601.
|
|
36
|
+
spec.add_dependency "json", "~> 2.0"
|
|
34
37
|
spec.add_dependency "kaminari", "~> 1.2"
|
|
35
38
|
spec.add_dependency "superglue", "~> 1.1"
|
|
36
39
|
spec.add_dependency "form_props", ">= 0.0.6"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: terrazzo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Terrazzo Contributors
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '7.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: json
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: kaminari
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|