terrazzo 0.7.2 → 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 +43 -5
- 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 +16 -2
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
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A drop-in admin panel for Rails apps. Uses the [Administrate](https://github.com/thoughtbot/administrate) dashboard DSL with a React SPA frontend powered by [Superglue](https://github.com/thoughtbot/superglue).
|
|
4
4
|
|
|
5
|
+
[](https://github.com/gohypelab/terrazzo/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/gohypelab/terrazzo/actions/workflows/release.yml)
|
|
7
|
+
|
|
5
8
|
- **Familiar DSL** — same `ATTRIBUTE_TYPES`, `COLLECTION_ATTRIBUTES`, `FORM_ATTRIBUTES` you already know
|
|
6
9
|
- **React SPA** — search, sort, and paginate without full page reloads, no separate API needed
|
|
7
10
|
- **shadcn/ui + Tailwind** — polished defaults with supported ejection when you want app-owned source
|
|
@@ -23,11 +26,13 @@ bundle exec vite install
|
|
|
23
26
|
|
|
24
27
|
# Add the npm package and frontend dependencies
|
|
25
28
|
npm install terrazzo
|
|
26
|
-
npm install @radix-ui/react-avatar @radix-ui/react-dialog
|
|
27
|
-
@radix-ui/react-
|
|
28
|
-
@radix-ui/react-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
npm install @radix-ui/react-avatar@1.1.11 @radix-ui/react-dialog@1.1.15 \
|
|
30
|
+
@radix-ui/react-dropdown-menu@2.1.16 @radix-ui/react-label@2.1.8 \
|
|
31
|
+
@radix-ui/react-popover@1.1.15 @radix-ui/react-select@2.2.6 \
|
|
32
|
+
@radix-ui/react-separator@1.1.8 @radix-ui/react-slot@1.2.4 \
|
|
33
|
+
@radix-ui/react-tooltip@1.2.8 class-variance-authority@0.7.1 \
|
|
34
|
+
lucide-react@0.344.0 tailwindcss@4.2.1
|
|
35
|
+
npm install --save-dev @tailwindcss/cli@4.2.1 # if your app does not already compile Tailwind
|
|
31
36
|
|
|
32
37
|
# Install Terrazzo — generates admin namespace, app barrels,
|
|
33
38
|
# shadcn metadata/path aliases, and dashboards.
|
|
@@ -58,6 +63,39 @@ Full docs at **[gohypelab.github.io/terrazzo](https://gohypelab.github.io/terraz
|
|
|
58
63
|
- Node.js 18+
|
|
59
64
|
- A JS bundler (Vite recommended, esbuild also supported)
|
|
60
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
|
+
|
|
69
|
+
## Development and releases
|
|
70
|
+
|
|
71
|
+
Pull requests and pushes to `main` run the Ruby unit specs, npm package checks,
|
|
72
|
+
documentation build, and example app system specs through GitHub Actions.
|
|
73
|
+
|
|
74
|
+
Repository npm projects pin direct dependencies and use a seven-day minimum
|
|
75
|
+
release age when resolving updates. The published package and documentation
|
|
76
|
+
projects also commit their lockfiles. Use npm 11.10 or newer when changing
|
|
77
|
+
dependencies; project `.npmrc` files save new dependencies with exact versions
|
|
78
|
+
automatically.
|
|
79
|
+
|
|
80
|
+
Terrazzo releases the Ruby gem and npm package from the same version tag. After
|
|
81
|
+
updating `lib/terrazzo/version.rb` and `npm/package.json` to matching versions,
|
|
82
|
+
push an annotated `v*` tag. The release workflow validates both packages,
|
|
83
|
+
publishes missing versions to RubyGems and npm through OIDC trusted publishing,
|
|
84
|
+
and creates a GitHub Release with generated notes. Existing registry versions
|
|
85
|
+
are skipped, so a partially completed or manually published release can be
|
|
86
|
+
retried safely.
|
|
87
|
+
|
|
88
|
+
See [RELEASING.md](RELEASING.md) for trusted publisher setup, release commands,
|
|
89
|
+
and retry instructions.
|
|
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
|
+
|
|
61
99
|
## Customizing Per-Row Actions
|
|
62
100
|
|
|
63
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
|
|
@@ -339,7 +353,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
339
353
|
- !ruby/object:Gem::Version
|
|
340
354
|
version: '0'
|
|
341
355
|
requirements: []
|
|
342
|
-
rubygems_version:
|
|
356
|
+
rubygems_version: 3.6.9
|
|
343
357
|
specification_version: 4
|
|
344
358
|
summary: A Rails admin framework powered by Superglue and React
|
|
345
359
|
test_files: []
|