tramway 4.0.1.4 → 4.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -0
  3. data/app/assets/javascripts/tramway/tramway.js +140 -2
  4. data/app/controllers/tramway/entities_controller.rb +35 -6
  5. data/app/controllers/tramway/plugins/solid_queue/application_controller.rb +28 -0
  6. data/app/controllers/tramway/plugins/solid_queue/jobs_controller.rb +82 -0
  7. data/app/controllers/tramway/plugins/solid_queue/queues_controller.rb +38 -0
  8. data/app/controllers/tramway/plugins/solid_queue/recurring_tasks_controller.rb +26 -0
  9. data/app/helpers/tramway/plugins/solid_queue/jobs_helper.rb +27 -0
  10. data/app/helpers/tramway/plugins/solid_queue/queues_helper.rb +27 -0
  11. data/app/jobs/tramway/solid_queue/application_job.rb +14 -0
  12. data/app/jobs/tramway/solid_queue/bulk_destroy_job.rb +13 -0
  13. data/app/jobs/tramway/solid_queue/bulk_discard_job.rb +13 -0
  14. data/app/jobs/tramway/solid_queue/bulk_retry_job.rb +13 -0
  15. data/app/jobs/tramway/solid_queue/destroy_all_job.rb +13 -0
  16. data/app/models/tramway/solid_queue/jobs_filter.rb +53 -0
  17. data/app/views/tramway/entities/_filters.html.haml +15 -0
  18. data/app/views/tramway/entities/_list.html.haml +22 -8
  19. data/app/views/tramway/plugins/solid_queue/_subnav.html.haml +7 -0
  20. data/app/views/tramway/plugins/solid_queue/jobs/index.html.haml +89 -0
  21. data/app/views/tramway/plugins/solid_queue/jobs/show.html.haml +90 -0
  22. data/app/views/tramway/plugins/solid_queue/queues/index.html.haml +46 -0
  23. data/app/views/tramway/plugins/solid_queue/recurring_tasks/index.html.haml +33 -0
  24. data/config/locales/en.yml +64 -0
  25. data/config/routes.rb +10 -0
  26. data/config/tailwind.config.js +4 -0
  27. data/lib/generators/tramway/install/install_generator.rb +17 -3
  28. data/lib/generators/tramway/install/solid_queue_helpers.rb +54 -0
  29. data/lib/tramway/config.rb +7 -1
  30. data/lib/tramway/configs/entities/page.rb +1 -0
  31. data/lib/tramway/configs/plugins.rb +66 -0
  32. data/lib/tramway/errors.rb +56 -0
  33. data/lib/tramway/navbar.rb +23 -0
  34. data/lib/tramway/plugins/base.rb +33 -0
  35. data/lib/tramway/plugins/solid_queue/config.rb +13 -0
  36. data/lib/tramway/plugins/solid_queue/plugin.rb +37 -0
  37. data/lib/tramway/plugins/solid_queue/routes.rb +50 -0
  38. data/lib/tramway/plugins/solid_queue.rb +7 -0
  39. data/lib/tramway/plugins.rb +37 -0
  40. data/lib/tramway/version.rb +1 -1
  41. metadata +27 -1
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tramway/plugins'
4
+
5
+ module Tramway
6
+ module Configs
7
+ # Value object stored at `Tramway.config.plugins`. It behaves like an array of
8
+ # enabled plugin names (`config.plugins = [:solid_queue]`, `config.plugins.include?`,
9
+ # `config.plugins.each`) while also exposing per-plugin configuration as attributes
10
+ # (`config.plugins.solid_queue = { path: '/jobs' }`, `config.plugins.solid_queue`).
11
+ class Plugins
12
+ include Enumerable
13
+
14
+ def initialize(names = [])
15
+ @names = Array(names).map(&:to_sym)
16
+ @configs = {}
17
+ end
18
+
19
+ def each(&block)
20
+ return to_enum(:each) unless block
21
+
22
+ @names.each(&block)
23
+ end
24
+
25
+ def include?(name)
26
+ @names.include?(name.to_sym)
27
+ end
28
+ alias enabled? include?
29
+
30
+ def to_a
31
+ @names.dup
32
+ end
33
+
34
+ def method_missing(method_name, *args)
35
+ plugin_name = plugin_name_from(method_name)
36
+
37
+ return super unless plugin_name
38
+
39
+ assignment?(method_name) ? assign_plugin_config(plugin_name, args.first) : plugin_config(plugin_name)
40
+ end
41
+
42
+ def respond_to_missing?(method_name, include_private = false)
43
+ plugin_name_from(method_name).present? || super
44
+ end
45
+
46
+ private
47
+
48
+ def plugin_name_from(method_name)
49
+ name = method_name.to_s.delete_suffix('=').to_sym
50
+ name if Tramway::Plugins.registered?(name)
51
+ end
52
+
53
+ def assignment?(method_name)
54
+ method_name.to_s.end_with?('=')
55
+ end
56
+
57
+ def plugin_config(name)
58
+ @configs[name] ||= Tramway::Plugins.fetch(name).config_class.new
59
+ end
60
+
61
+ def assign_plugin_config(name, options)
62
+ @configs[name] = Tramway::Plugins.fetch(name).config_class.new(**(options || {}).symbolize_keys)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -41,5 +41,61 @@ module Tramway
41
41
  MESSAGE
42
42
  end
43
43
  end
44
+
45
+ # Raised when `params[:filter]` on an entities#index page does not match any of the
46
+ # filters declared for that page in the Tramway config, so the developer (or the user
47
+ # following a crafted link) gets a clear explanation instead of a raw NoMethodError from
48
+ # calling an arbitrary, possibly non-existent scope.
49
+ class InvalidFilterError < StandardError
50
+ def initialize(filter:, available_filters:, model_class:)
51
+ super(<<~MESSAGE)
52
+ Tramway received an unknown filter "#{filter}" for #{model_class} on the \
53
+ entities#index page.
54
+
55
+ Available filters: #{available_filters.join(', ')}.
56
+
57
+ To fix this:
58
+ - pass one of the available filters above in `params[:filter]`, or
59
+ - add `:#{filter}` to this entity's `filters:` list for the `:index` page in your \
60
+ Tramway config if it should be selectable.
61
+ MESSAGE
62
+ end
63
+ end
64
+
65
+ # Raised when `Tramway.config.plugins` references a plugin name that was never registered
66
+ # via `Tramway::Plugins.register`, so the developer gets a clear explanation instead of a
67
+ # raw NoMethodError/KeyError deep inside the plugin config collection.
68
+ class UnknownPluginError < StandardError
69
+ def initialize(name:, available:)
70
+ super(<<~MESSAGE)
71
+ Tramway received an unknown plugin "#{name}".
72
+
73
+ Available plugins: #{available.any? ? available.join(', ') : '(none registered)'}.
74
+
75
+ To fix this:
76
+ - use one of the available plugins above in `Tramway.config.plugins`, or
77
+ - register a custom plugin via `Tramway::Plugins.register(YourPluginClass)` before \
78
+ referencing it in the config.
79
+ MESSAGE
80
+ end
81
+ end
82
+
83
+ # Raised when a Tramway plugin depends on a gem that is not installed in the host
84
+ # application, so the developer gets a precise, actionable fix instead of a raw
85
+ # LoadError/NameError deep inside the plugin's controllers.
86
+ class MissingPluginDependencyError < StandardError
87
+ def initialize(plugin:, gem_name:, original_error:)
88
+ super(<<~MESSAGE)
89
+ The Tramway "#{plugin}" plugin requires the `#{gem_name}` gem, but it could not be \
90
+ loaded (#{original_error.class}: #{original_error.message}).
91
+
92
+ To fix this:
93
+ - add `gem "#{gem_name}"` to your Gemfile and run `bundle install`, then restart \
94
+ your server, or
95
+ - remove `:#{plugin}` from `Tramway.config.plugins` if you do not want to use this \
96
+ plugin.
97
+ MESSAGE
98
+ end
99
+ end
44
100
  end
45
101
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tramway/plugins'
4
+
3
5
  module Tramway
4
6
  # Navbar object provides left and right elements
5
7
  class Navbar
@@ -10,6 +12,8 @@ module Tramway
10
12
  @items = { left: [], right: [] }
11
13
  @filling = nil
12
14
 
15
+ preset_plugins
16
+
13
17
  return unless with_entities
14
18
 
15
19
  entities = Tramway.config.entities
@@ -61,6 +65,25 @@ module Tramway
61
65
  reset_filling
62
66
  end
63
67
 
68
+ def preset_plugins
69
+ plugins = Tramway.config.plugins
70
+
71
+ return unless plugins.any?
72
+
73
+ filling_side :left
74
+
75
+ plugins.each do |name|
76
+ plugin = Tramway::Plugins.fetch(name)
77
+ nav_item = plugin.nav_item(plugins.public_send(name))
78
+
79
+ next if nav_item.blank?
80
+
81
+ item nav_item[:text], nav_item[:path], icon: nav_item[:icon]
82
+ end
83
+
84
+ reset_filling
85
+ end
86
+
64
87
  def provided_url_and_block?(url)
65
88
  url.present? && block_given?
66
89
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway
4
+ module Plugins
5
+ # Interface every Tramway plugin must implement to register itself with
6
+ # `Tramway::Plugins.register` and be referenced from `Tramway.config.plugins`.
7
+ class Base
8
+ class << self
9
+ def plugin_name
10
+ raise NotImplementedError, "#{self} must implement .plugin_name"
11
+ end
12
+
13
+ def config_class
14
+ raise NotImplementedError, "#{self} must implement .config_class"
15
+ end
16
+
17
+ def draw_routes(_mapper, _config)
18
+ raise NotImplementedError, "#{self} must implement .draw_routes"
19
+ end
20
+
21
+ # Returns a Hash with :text, :path and :icon keys used to render a navbar entry
22
+ # for this plugin, or nil to not render one.
23
+ def nav_item(_config)
24
+ nil
25
+ end
26
+
27
+ # Raises a clear, actionable error when the plugin's required infrastructure
28
+ # (typically a gem) is missing. No-op by default.
29
+ def ensure_available!; end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway
4
+ module Plugins
5
+ module SolidQueue
6
+ # Per-plugin configuration for the `:solid_queue` plugin, set via
7
+ # `Tramway.config.plugins.solid_queue = { path: '/jobs' }`.
8
+ class Config < Dry::Struct
9
+ attribute? :path, Types::Coercible::String.default('/jobs')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway
4
+ module Plugins
5
+ module SolidQueue
6
+ # Registry entry for the `:solid_queue` plugin — a Tramway-based dashboard for
7
+ # SolidQueue jobs, queues and recurring tasks.
8
+ class Plugin < Tramway::Plugins::Base
9
+ class << self
10
+ def plugin_name
11
+ :solid_queue
12
+ end
13
+
14
+ def config_class
15
+ Tramway::Plugins::SolidQueue::Config
16
+ end
17
+
18
+ def draw_routes(mapper, config)
19
+ Tramway::Plugins::SolidQueue::Routes.draw(mapper, config)
20
+ end
21
+
22
+ def nav_item(config)
23
+ { text: 'Background Jobs', path: config.path, icon: 'fa-tasks' }
24
+ end
25
+
26
+ def ensure_available!
27
+ require 'solid_queue'
28
+ rescue LoadError => e
29
+ raise Tramway::Errors::MissingPluginDependencyError.new(
30
+ plugin: plugin_name, gem_name: 'solid_queue', original_error: e
31
+ )
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tramway
4
+ module Plugins
5
+ module SolidQueue
6
+ # Draws the routes for the solid_queue dashboard plugin, mounted at the
7
+ # plugin's configured `path` (defaults to `/jobs`).
8
+ module Routes
9
+ module_function
10
+
11
+ def draw(mapper, _config)
12
+ draw_queues(mapper)
13
+ draw_recurring_tasks(mapper)
14
+ draw_jobs(mapper)
15
+ end
16
+
17
+ def draw_queues(mapper)
18
+ mapper.instance_exec do
19
+ resources :queues, only: %i[index], param: :name, module: 'plugins/solid_queue' do
20
+ post :pause, on: :member
21
+ post :resume, on: :member
22
+ post :clear, on: :member
23
+ end
24
+ end
25
+ end
26
+
27
+ def draw_recurring_tasks(mapper)
28
+ mapper.instance_exec do
29
+ resources :recurring_tasks, only: %i[index], param: :key, module: 'plugins/solid_queue' do
30
+ post :enqueue, on: :member
31
+ end
32
+ end
33
+ end
34
+
35
+ def draw_jobs(mapper)
36
+ mapper.instance_exec do
37
+ resources :jobs, path: '', only: %i[index show destroy], module: 'plugins/solid_queue' do
38
+ post :retry, on: :member
39
+ post :discard, on: :member
40
+ post :bulk_retry, on: :collection
41
+ post :bulk_discard, on: :collection
42
+ post :bulk_destroy, on: :collection
43
+ post :destroy_all, on: :collection
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tramway/plugins/solid_queue/config'
4
+ require 'tramway/plugins/solid_queue/routes'
5
+ require 'tramway/plugins/solid_queue/plugin'
6
+
7
+ Tramway::Plugins.register(Tramway::Plugins::SolidQueue::Plugin)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tramway/errors'
4
+ require 'tramway/plugins/base'
5
+
6
+ module Tramway
7
+ # Registry for Tramway plugins. A plugin is a class implementing the
8
+ # `Tramway::Plugins::Base` interface, registered via `Tramway::Plugins.register`,
9
+ # and referenced by name from `Tramway.config.plugins`.
10
+ module Plugins
11
+ module_function
12
+
13
+ def registry
14
+ @registry ||= {}
15
+ end
16
+
17
+ def register(plugin_class)
18
+ registry[plugin_class.plugin_name.to_sym] = plugin_class
19
+ end
20
+
21
+ def registered?(name)
22
+ registry.key?(name.to_sym)
23
+ end
24
+
25
+ def fetch(name)
26
+ registry.fetch(name.to_sym) do
27
+ raise Tramway::Errors::UnknownPluginError.new(name:, available: registry.keys)
28
+ end
29
+ end
30
+
31
+ def each(&)
32
+ registry.each(&)
33
+ end
34
+ end
35
+ end
36
+
37
+ require 'tramway/plugins/solid_queue'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '4.0.1.4'
4
+ VERSION = '4.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tramway
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1.4
4
+ version: '4.1'
5
5
  platform: ruby
6
6
  authors:
7
7
  - kalashnikovisme
@@ -279,7 +279,19 @@ files:
279
279
  - app/components/tramway/tooltip_component.html.haml
280
280
  - app/components/tramway/tooltip_component.rb
281
281
  - app/controllers/tramway/entities_controller.rb
282
+ - app/controllers/tramway/plugins/solid_queue/application_controller.rb
283
+ - app/controllers/tramway/plugins/solid_queue/jobs_controller.rb
284
+ - app/controllers/tramway/plugins/solid_queue/queues_controller.rb
285
+ - app/controllers/tramway/plugins/solid_queue/recurring_tasks_controller.rb
282
286
  - app/helpers/tramway/application_helper.rb
287
+ - app/helpers/tramway/plugins/solid_queue/jobs_helper.rb
288
+ - app/helpers/tramway/plugins/solid_queue/queues_helper.rb
289
+ - app/jobs/tramway/solid_queue/application_job.rb
290
+ - app/jobs/tramway/solid_queue/bulk_destroy_job.rb
291
+ - app/jobs/tramway/solid_queue/bulk_discard_job.rb
292
+ - app/jobs/tramway/solid_queue/bulk_retry_job.rb
293
+ - app/jobs/tramway/solid_queue/destroy_all_job.rb
294
+ - app/models/tramway/solid_queue/jobs_filter.rb
283
295
  - app/views/kaminari/_first_page.html.haml
284
296
  - app/views/kaminari/_gap.html.haml
285
297
  - app/views/kaminari/_last_page.html.haml
@@ -289,6 +301,7 @@ files:
289
301
  - app/views/kaminari/_prev_page.html.haml
290
302
  - app/views/tramway/chats/_message.html.haml
291
303
  - app/views/tramway/chats/_messages.html.haml
304
+ - app/views/tramway/entities/_filters.html.haml
292
305
  - app/views/tramway/entities/_form.html.haml
293
306
  - app/views/tramway/entities/_list.html.haml
294
307
  - app/views/tramway/entities/edit.html.haml
@@ -296,6 +309,11 @@ files:
296
309
  - app/views/tramway/entities/new.html.haml
297
310
  - app/views/tramway/entities/show.html.haml
298
311
  - app/views/tramway/layouts/application.html.haml
312
+ - app/views/tramway/plugins/solid_queue/_subnav.html.haml
313
+ - app/views/tramway/plugins/solid_queue/jobs/index.html.haml
314
+ - app/views/tramway/plugins/solid_queue/jobs/show.html.haml
315
+ - app/views/tramway/plugins/solid_queue/queues/index.html.haml
316
+ - app/views/tramway/plugins/solid_queue/recurring_tasks/index.html.haml
299
317
  - config/locales/en.yml
300
318
  - config/routes.rb
301
319
  - config/tailwind.config.js
@@ -303,6 +321,7 @@ files:
303
321
  - lib/generators/tramway/install/font_awesome_helpers.rb
304
322
  - lib/generators/tramway/install/install_generator.rb
305
323
  - lib/generators/tramway/install/layout_helpers.rb
324
+ - lib/generators/tramway/install/solid_queue_helpers.rb
306
325
  - lib/kaminari/helpers/tag.rb
307
326
  - lib/rules/turbo_html_attributes_rules.rb
308
327
  - lib/tasks/tramway_tasks.rake
@@ -314,6 +333,7 @@ files:
314
333
  - lib/tramway/configs/entities/page.rb
315
334
  - lib/tramway/configs/entities/route.rb
316
335
  - lib/tramway/configs/entity.rb
336
+ - lib/tramway/configs/plugins.rb
317
337
  - lib/tramway/decorators/association.rb
318
338
  - lib/tramway/decorators/class_helper.rb
319
339
  - lib/tramway/decorators/collection_decorator.rb
@@ -337,6 +357,12 @@ files:
337
357
  - lib/tramway/helpers/views_helper.rb
338
358
  - lib/tramway/navbar.rb
339
359
  - lib/tramway/pg_searchable.rb
360
+ - lib/tramway/plugins.rb
361
+ - lib/tramway/plugins/base.rb
362
+ - lib/tramway/plugins/solid_queue.rb
363
+ - lib/tramway/plugins/solid_queue/config.rb
364
+ - lib/tramway/plugins/solid_queue/plugin.rb
365
+ - lib/tramway/plugins/solid_queue/routes.rb
340
366
  - lib/tramway/searchable.rb
341
367
  - lib/tramway/utils/field.rb
342
368
  - lib/tramway/utils/render.rb