trek 2.0.1 → 2.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.prettierignore +2 -0
  3. data/.rubocop.yml +11 -0
  4. data/AGENTS.md +5 -2
  5. data/CHANGELOG.md +47 -1
  6. data/Gemfile.lock +1 -1
  7. data/Makefile +13 -0
  8. data/app/assets/icons/trek/audit.svg +1 -0
  9. data/app/assets/icons/trek/print.svg +1 -0
  10. data/app/components/trek/dashboard/analytics_component/analytics_component.html.slim +2 -0
  11. data/app/components/trek/dashboard/analytics_component.rb +1 -0
  12. data/app/components/trek/dashboard/chart_component/chart_component.css +192 -0
  13. data/app/components/trek/dashboard/chart_component/chart_component.html.slim +6 -0
  14. data/app/components/trek/dashboard/chart_component/chart_component.js +1 -0
  15. data/app/components/trek/dashboard/chart_component.rb +27 -0
  16. data/app/components/trek/dashboard/tile_component/tile_component.css +40 -0
  17. data/app/components/trek/dashboard/tile_component/tile_component.html.slim +14 -4
  18. data/app/components/trek/dashboard/tile_component.rb +6 -2
  19. data/app/components/trek/entries/item_component/item_component.css +1 -0
  20. data/app/components/trek/form/actions_component/actions_component.css +6 -7
  21. data/app/components/trek/form/content_editor_component/content_editor_component.html.slim +10 -12
  22. data/app/components/trek/form/content_editor_component/content_editor_component.js +6 -0
  23. data/app/components/trek/form/content_editor_component.rb +4 -2
  24. data/app/components/trek/form/fieldset_component/fieldset_component.css +4 -3
  25. data/app/components/trek/form/group_component/group_component.css +5 -2
  26. data/app/components/trek/layout_component/layout_component.css +20 -0
  27. data/app/components/trek/layout_component/layout_component.html.slim +16 -0
  28. data/app/components/trek/layout_component/layout_component.js +35 -6
  29. data/app/components/trek/menu_component.rb +15 -2
  30. data/app/controllers/concerns/trek/model.rb +13 -6
  31. data/app/controllers/trek/resource_controller.rb +9 -1
  32. data/app/formatters/prosemirror_to_html/global_id_to_links_formatter.rb +3 -1
  33. data/app/helpers/trek/i18n_helper.rb +1 -1
  34. data/app/models/prosemirror.rb +5 -2
  35. data/app/views/trek/panels/images/_form.html.slim +1 -1
  36. data/app/views/trek/panels/images/create.turbo_stream.slim +3 -3
  37. data/app/views/trek/panels/images/new.html.slim +1 -1
  38. data/app/views/trek/panels/links/_form.html.slim +1 -1
  39. data/app/views/trek/panels/links/create.turbo_stream.slim +3 -3
  40. data/app/views/trek/panels/links/new.html.slim +1 -1
  41. data/app/views/trek/panels/prompts/_form.html.slim +1 -1
  42. data/app/views/trek/panels/prompts/create.turbo_stream.slim +3 -3
  43. data/app/views/trek/panels/prompts/new.html.slim +1 -1
  44. data/docs/.vitepress/config.mjs +2 -1
  45. data/docs/deploy.md +57 -0
  46. data/docs/getting-started.md +1 -0
  47. data/docs/reference/controllers.md +23 -0
  48. data/docs/reference/generators/install.md +2 -0
  49. data/docs/reference/generators/scaffold.md +12 -0
  50. data/lib/generators/trek/install/config_generator.rb +9 -1
  51. data/lib/generators/trek/install/postmark_generator.rb +2 -0
  52. data/lib/generators/trek/install/prosopite_generator.rb +56 -0
  53. data/lib/generators/trek/install/scalingo_generator.rb +19 -7
  54. data/lib/generators/trek/install/yarn_generator.rb +1 -1
  55. data/lib/generators/trek/install_generator.rb +1 -0
  56. data/lib/generators/trek/scaffold_generator.rb +37 -5
  57. data/lib/generators/trek/templates/controllers/admin/scaffold_controller.rb.tt +2 -2
  58. data/lib/generators/trek/templates/locales/scaffold/model.en.yml.tt +4 -3
  59. data/lib/generators/trek/templates/spec/requests/admin/n_plus_one_spec.rb +28 -0
  60. data/lib/generators/trek/templates/views/admin/scaffold/show.html.slim.tt +2 -2
  61. data/lib/generators/trek/upgrade/v2_generator.rb +31 -0
  62. data/lib/trek/version.rb +1 -1
  63. data/package.json +1 -1
  64. data/vendor/tom_select/css/tom-select.custom.css +0 -1
  65. metadata +11 -1
@@ -0,0 +1,56 @@
1
+ require "rails/generators/actions"
2
+ require "generators/trek/helpers"
3
+
4
+ module Trek
5
+ module Generators
6
+ module Install
7
+ class ProsopiteGenerator < Rails::Generators::Base
8
+ include Trek::Generators::Helpers
9
+
10
+ desc "Install Prosopite (N+1 query detection)."
11
+
12
+ source_root File.expand_path("../templates", __dir__)
13
+
14
+ def add_gems
15
+ return if in_gemfile?("prosopite")
16
+
17
+ gem "prosopite", group: [:development, :test],
18
+ comment: "N+1 query detection (raises in test, logs in development) [https://github.com/charkost/prosopite]"
19
+ gem "pg_query", group: [:development, :test]
20
+ bundle_install
21
+ end
22
+
23
+ def create_initializer
24
+ return if File.exist?("config/initializers/prosopite.rb")
25
+
26
+ create_file "config/initializers/prosopite.rb" do
27
+ <<~RUBY
28
+ # N+1 query detection with Prosopite, in development and test only (the gem is not loaded in production).
29
+ # Every request is scanned by the Rack middleware, which also covers Trek's engine controllers.
30
+ # Detected N+1s raise Prosopite::NPlusOneQueriesError in test and are logged in development.
31
+ # Legitimate repeated queries can be wrapped in `Prosopite.pause { … }`.
32
+ if defined?(Prosopite)
33
+ require "prosopite/middleware/rack"
34
+
35
+ Rails.application.configure do
36
+ config.middleware.use(Prosopite::Middleware::Rack)
37
+ end
38
+
39
+ Rails.application.config.after_initialize do
40
+ Prosopite.rails_logger = true
41
+ Prosopite.raise = Rails.env.test?
42
+ end
43
+ end
44
+ RUBY
45
+ end
46
+ end
47
+
48
+ def create_smoke_spec
49
+ return if File.exist?("spec/requests/admin/n_plus_one_spec.rb")
50
+
51
+ copy_file "spec/requests/admin/n_plus_one_spec.rb", "spec/requests/admin/n_plus_one_spec.rb"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -5,17 +5,16 @@ module Trek
5
5
  module Generators
6
6
  module Install
7
7
  class ScalingoGenerator < Rails::Generators::Base
8
+ BUILDPACK_URL = "https://github.com/etaminstudio/trek-buildpack#v0.1.0".freeze
9
+
8
10
  include Trek::Generators::Helpers
9
11
 
10
12
  desc "Create Scalingo files."
11
13
 
12
- def create_buildpacks
13
- create_file ".buildpacks" do
14
- <<~TEXT
15
- https://github.com/Scalingo/apt-buildpack.git
16
- https://github.com/Scalingo/ruby-buildpack.git
17
- TEXT
18
- end
14
+ # The trek-buildpack replaces the apt + ruby buildpack combination. It
15
+ # pins Node from the .node-version that `rails new` already generates.
16
+ def remove_buildpacks
17
+ remove_file ".buildpacks"
19
18
  end
20
19
 
21
20
  def create_aptfile
@@ -37,6 +36,19 @@ module Trek
37
36
  TEXT
38
37
  end
39
38
  end
39
+
40
+ def final_instructions
41
+ say ""
42
+ say "Configure the Scalingo app environment:"
43
+ say ""
44
+ say " scalingo --app <app> env-set BUILDPACK_URL=#{BUILDPACK_URL}"
45
+ say " scalingo --app <app> env-set 'DATABASE_URL=$SCALINGO_POSTGRESQL_URL'"
46
+ say ""
47
+ say "and make sure config/database.yml reads DATABASE_URL in production."
48
+ say ""
49
+ say "See https://trek.etamin.studio/deploy for details."
50
+ say ""
51
+ end
40
52
  end
41
53
  end
42
54
  end
@@ -5,7 +5,7 @@ module Trek
5
5
  module Generators
6
6
  module Install
7
7
  class YarnGenerator < Rails::Generators::Base
8
- YARN_VERSION = "4.17.0"
8
+ YARN_VERSION = "4.18.0"
9
9
 
10
10
  include Trek::Generators::Helpers
11
11
 
@@ -25,6 +25,7 @@ module Trek
25
25
  generate "trek:install:postcss"
26
26
  generate "trek:install:prettier"
27
27
  generate "trek:install:procfiles"
28
+ generate "trek:install:prosopite"
28
29
  generate "trek:install:rubocop"
29
30
  generate "trek:install:slim_lint"
30
31
  generate "trek:install:stylelint"
@@ -15,6 +15,18 @@ module Trek
15
15
 
16
16
  source_root File.expand_path("templates", __dir__)
17
17
 
18
+ # `rails g model Shop::Product` writes app/models/shop.rb as a `module Shop`
19
+ # namespace file; if a `Shop` model already lives there, it gets replaced.
20
+ def warn_about_shadowed_models
21
+ model_namespaces.each do |ns|
22
+ path = "app/models/#{ns}.rb"
23
+ next unless File.exist?(path) && File.read(path).match?(/^class #{ns.camelize}\b/)
24
+
25
+ say_status :warning, "#{path} defines the #{ns.camelize} model, which the #{ns.camelize} namespace " \
26
+ "would replace — answer 'n' below to keep it, or move the model first", :red
27
+ end
28
+ end
29
+
18
30
  def create_model
19
31
  generate :model, class_name, attributes.map { |a| "#{a.name}:#{a.type}" }
20
32
  end
@@ -32,7 +44,8 @@ module Trek
32
44
 
33
45
  def create_controller
34
46
  template "controllers/admin/scaffold_controller.rb",
35
- "app/controllers/admin/#{namespaced_plural}_controller.rb"
47
+ controller_file_path
48
+ reindent controller_file_path
36
49
  end
37
50
 
38
51
  def create_views
@@ -42,9 +55,11 @@ module Trek
42
55
  end
43
56
  end
44
57
 
58
+ # Top-level qualified: once an Admin::Shop namespace exists (scaffolded
59
+ # controller/policy), a bare `Shop` under `module Admin` resolves to it.
45
60
  def add_models_to_menu
46
61
  inject_into_file dashboard_file_path,
47
- "\n#{class_name},",
62
+ "\n::#{class_name},",
48
63
  after: "MODELS_IN_MENU = ["
49
64
  run "rubocop --autocorrect #{dashboard_file_path}", abort_on_failure: true
50
65
  end
@@ -67,7 +82,8 @@ module Trek
67
82
 
68
83
  def add_policy
69
84
  template "policies/admin/scaffold_policy.rb",
70
- "app/policies/admin/#{file_path}_policy.rb"
85
+ policy_file_path
86
+ reindent policy_file_path
71
87
  end
72
88
 
73
89
  def add_default_locale_files
@@ -114,6 +130,22 @@ module Trek
114
130
  "app/models/admin/dashboard.rb"
115
131
  end
116
132
 
133
+ # The templates wrap the class in one `module` per namespace segment
134
+ # without re-indenting the body; the indentation cops are forced with
135
+ # --only because rubocop-rails-omakase leaves them disabled.
136
+ def reindent(path)
137
+ run "rubocop --autocorrect --only Layout/IndentationWidth,Layout/IndentationConsistency #{path}",
138
+ abort_on_failure: true
139
+ end
140
+
141
+ def controller_file_path
142
+ "app/controllers/admin/#{namespaced_plural}_controller.rb"
143
+ end
144
+
145
+ def policy_file_path
146
+ "app/policies/admin/#{file_path}_policy.rb"
147
+ end
148
+
117
149
  def model_injection
118
150
  <<~RUBY
119
151
  # TODO: uncomment to format some attributes, or remove if not needed
@@ -160,12 +192,12 @@ module Trek
160
192
  "config/locales/admin/#{file_path}.#{locale}.yml"
161
193
  end
162
194
 
163
- # Returns namespace segments from file_path (e.g., ["missive"] for "missive/list")
195
+ # Returns namespace segments from file_path (e.g., ["shop"] for "shop/product")
164
196
  def model_namespaces
165
197
  file_path.split("/")[0...-1]
166
198
  end
167
199
 
168
- # Returns the pluralized path preserving namespaces (e.g., "missive/lists" for "missive/list")
200
+ # Returns the pluralized path preserving namespaces (e.g., "shop/products" for "shop/product")
169
201
  def namespaced_plural
170
202
  parts = file_path.split("/")
171
203
  parts[-1] = parts[-1].pluralize
@@ -2,13 +2,13 @@ module Admin
2
2
  <% model_namespaces.each do |ns| -%>
3
3
  module <%= ns.camelize %>
4
4
  <% end -%>
5
- class <%= controller_class_name %>Controller < Trek::ResourceController
5
+ class <%= controller_class_name.demodulize %>Controller < Trek::ResourceController
6
6
  # include Trek::Pagination
7
7
 
8
8
  private
9
9
 
10
10
  def model
11
- <%= class_name %>
11
+ ::<%= class_name %>
12
12
  end
13
13
  end
14
14
  <% model_namespaces.each do |_ns| -%>
@@ -2,10 +2,11 @@ en:
2
2
  activerecord:
3
3
  models:
4
4
  <%= file_path %>:
5
- gender: 'n'
5
+ gender: "n"
6
6
  one: <%= human_name %>
7
7
  other: <%= plural_name.humanize %>
8
8
  attributes:
9
- <%= file_path %>:<%- attributes.each do |attr| %>
9
+ <%= file_path %>:
10
+ <%- attributes.each do |attr| -%>
10
11
  <%= attr.name %>: <%= attr.name.humanize -%>
11
- <% end %>
12
+ <%- end -%>
@@ -0,0 +1,28 @@
1
+ require "rails_helper"
2
+
3
+ # Smoke test for Prosopite (config/initializers/prosopite.rb): each admin page below is rendered
4
+ # as an admin, and any N+1 query raises Prosopite::NPlusOneQueriesError.
5
+ #
6
+ # TODO: add the other admin routes (index and edit pages of scaffolded models in particular), and
7
+ # create a few records first so the per-row queries have something to loop over — an empty list
8
+ # can't reveal an N+1.
9
+ RSpec.describe "Admin pages N+1 queries", type: :request do
10
+ let(:password) { "secret-password-123" }
11
+ let(:admin) { User.create!(email: "admin@example.com", password:, role: "admin") }
12
+
13
+ before do
14
+ post admin_user_session_path, params: { email: admin.email, password: }
15
+ end
16
+
17
+ {
18
+ "dashboard" => -> { admin_root_path },
19
+ "pages index" => -> { admin_pages_path },
20
+ "fragments index" => -> { admin_fragments_path }
21
+ }.each do |page, path|
22
+ it "renders the #{page} without N+1 queries" do
23
+ get instance_exec(&path)
24
+
25
+ expect(response).to have_http_status(:ok)
26
+ end
27
+ end
28
+ end
@@ -3,8 +3,8 @@
3
3
  = render Trek::TabsComponent.new do |c|
4
4
  - c.with_item(id: :content, label: t("admin.content"))
5
5
  = render Trek::PropertiesComponent.new(model:) do |p|
6
- <% attributes.each do |attribute| -%>
6
+ <% attributes.each_with_index do |attribute, i| -%>
7
+ <%= "\n" unless i.zero? -%>
7
8
  = p.with_item :<%= attribute.column_name %>
8
9
  = @object.<%= attribute.column_name %>
9
-
10
10
  <% end -%>
@@ -1,5 +1,6 @@
1
1
  require "rails/generators/actions"
2
2
  require "generators/trek/helpers"
3
+ require "generators/trek/install/yarn_generator"
3
4
  require "yaml"
4
5
 
5
6
  module Trek
@@ -66,10 +67,28 @@ module Trek
66
67
  "and the etaminstudio scope manually.", :yellow
67
68
  end
68
69
 
70
+ def upgrade_yarn
71
+ return if yarn_major_version >= 4
72
+
73
+ # The npmPreapprovedPackages setting below requires Yarn 4 (Yarn 3
74
+ # hard-errors on it), so apply the 0.1.26 upgrade note as part of the
75
+ # migration: bump Yarn and drop the interactive-tools plugin, whose
76
+ # upgrade-interactive command is built into Yarn 4.
77
+ if File.exist?(".yarnrc.yml") && File.read(".yarnrc.yml").include?("plugin-interactive-tools")
78
+ run "yarn plugin remove @yarnpkg/plugin-interactive-tools"
79
+ end
80
+ run "yarn set version #{Install::YarnGenerator::YARN_VERSION}", abort_on_failure: true
81
+ @yarn_version = nil
82
+ end
83
+
69
84
  def preapprove_npm_package
70
85
  return unless File.exist?(".yarnrc.yml")
71
86
  return if File.read(".yarnrc.yml").include?("npmPreapprovedPackages")
72
87
 
88
+ # Defensive: never write the (Yarn 4-only) setting under Yarn 3,
89
+ # where it is a hard error that breaks every yarn command.
90
+ return if yarn_major_version < 4
91
+
73
92
  append_to_file ".yarnrc.yml", <<~YAML
74
93
 
75
94
  # Trek is our own package — exempt it from Yarn's package gates
@@ -98,6 +117,18 @@ module Trek
98
117
  * Revoke this project's Trek deploy token on GitLab.
99
118
  SUMMARY
100
119
  end
120
+
121
+ private
122
+
123
+ def yarn_version
124
+ @yarn_version ||= `yarn --version`.strip
125
+ rescue Errno::ENOENT
126
+ @yarn_version = "0"
127
+ end
128
+
129
+ def yarn_major_version
130
+ yarn_version.to_i
131
+ end
101
132
  end
102
133
  end
103
134
  end
data/lib/trek/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Trek
4
- VERSION = "2.0.1"
4
+ VERSION = "2.2.0"
5
5
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trek",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "A modern content management & back-office system for Ruby on Rails",
5
5
  "homepage": "https://trek.etamin.studio",
6
6
  "license": "MIT",
@@ -459,4 +459,3 @@
459
459
  .ts-dropdown .optgroup:first-child {
460
460
  border-top: 0 none;
461
461
  }
462
- /*# sourceMappingURL=tom-select.default.css.map */
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trek
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Bengrich
@@ -434,6 +434,7 @@ files:
434
434
  - ".eslintrc.json"
435
435
  - ".node-version"
436
436
  - ".npmignore"
437
+ - ".prettierignore"
437
438
  - ".rspec"
438
439
  - ".rubocop.yml"
439
440
  - ".slim-lint.yml"
@@ -453,6 +454,7 @@ files:
453
454
  - app/assets/icons/trek/add.svg
454
455
  - app/assets/icons/trek/arrow-bar-left.svg
455
456
  - app/assets/icons/trek/arrow-bar-right.svg
457
+ - app/assets/icons/trek/audit.svg
456
458
  - app/assets/icons/trek/bold.svg
457
459
  - app/assets/icons/trek/check.svg
458
460
  - app/assets/icons/trek/chevron-double-left.svg
@@ -476,6 +478,7 @@ files:
476
478
  - app/assets/icons/trek/logout.svg
477
479
  - app/assets/icons/trek/paragraph.svg
478
480
  - app/assets/icons/trek/pic.svg
481
+ - app/assets/icons/trek/print.svg
479
482
  - app/assets/icons/trek/profile.svg
480
483
  - app/assets/icons/trek/quotation.svg
481
484
  - app/assets/icons/trek/rating.svg
@@ -509,6 +512,10 @@ files:
509
512
  - app/components/trek/dashboard/analytics_component/analytics_component.css
510
513
  - app/components/trek/dashboard/analytics_component/analytics_component.html.slim
511
514
  - app/components/trek/dashboard/analytics_component/analytics_component.js
515
+ - app/components/trek/dashboard/chart_component.rb
516
+ - app/components/trek/dashboard/chart_component/chart_component.css
517
+ - app/components/trek/dashboard/chart_component/chart_component.html.slim
518
+ - app/components/trek/dashboard/chart_component/chart_component.js
512
519
  - app/components/trek/dashboard/section_component.rb
513
520
  - app/components/trek/dashboard/section_component/section_component.css
514
521
  - app/components/trek/dashboard/section_component/section_component.html.slim
@@ -737,6 +744,7 @@ files:
737
744
  - docs/.vitepress/theme/index.js
738
745
  - docs/.vitepress/theme/style.css
739
746
  - docs/customize.md
747
+ - docs/deploy.md
740
748
  - docs/getting-started.md
741
749
  - docs/index.md
742
750
  - docs/public/logo.svg
@@ -821,6 +829,7 @@ files:
821
829
  - lib/generators/trek/install/postmark_generator.rb
822
830
  - lib/generators/trek/install/prettier_generator.rb
823
831
  - lib/generators/trek/install/procfiles_generator.rb
832
+ - lib/generators/trek/install/prosopite_generator.rb
824
833
  - lib/generators/trek/install/readme_generator.rb
825
834
  - lib/generators/trek/install/rubocop_generator.rb
826
835
  - lib/generators/trek/install/ruby_generator.rb
@@ -902,6 +911,7 @@ files:
902
911
  - lib/generators/trek/templates/policies/page.rb
903
912
  - lib/generators/trek/templates/policies/preview.rb
904
913
  - lib/generators/trek/templates/policies/user.rb
914
+ - lib/generators/trek/templates/spec/requests/admin/n_plus_one_spec.rb
905
915
  - lib/generators/trek/templates/views/admin/dashboards/index.html.slim
906
916
  - lib/generators/trek/templates/views/admin/fragments/_form.html.slim
907
917
  - lib/generators/trek/templates/views/admin/fragments/_fragment.json.jb