message_train 0.7.5 → 1.0.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 (171) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/ci.yml +86 -0
  4. data/.gitignore +64 -0
  5. data/.rubocop.yml +9 -0
  6. data/.rubocop_todo.yml +854 -2
  7. data/.ruby-version +1 -1
  8. data/.simplecov +2 -4
  9. data/CHANGELOG.md +128 -0
  10. data/CLAUDE.md +5 -0
  11. data/Gemfile +30 -32
  12. data/LICENSE.txt +1 -1
  13. data/README.md +106 -35
  14. data/Rakefile +4 -18
  15. data/app/assets/stylesheets/message_train.scss +405 -67
  16. data/app/controllers/concerns/message_train_support.rb +2 -2
  17. data/app/controllers/message_train/boxes_controller.rb +4 -2
  18. data/app/controllers/message_train/conversations_controller.rb +1 -0
  19. data/app/helpers/message_train/application_helper.rb +68 -0
  20. data/app/helpers/message_train/attachments_helper.rb +7 -4
  21. data/app/helpers/message_train/conversations_helper.rb +16 -16
  22. data/app/helpers/message_train/messages_helper.rb +23 -10
  23. data/app/javascript/controllers/message_train/bulk_actions_controller.js +36 -0
  24. data/app/javascript/controllers/message_train/lightbox_controller.js +25 -0
  25. data/app/javascript/controllers/message_train/nested_form_controller.js +36 -0
  26. data/app/javascript/controllers/message_train/recipient_autocomplete_controller.js +31 -0
  27. data/app/mailers/message_train/previews/receipt_mailer_preview.rb +7 -5
  28. data/app/models/message_train/attachment.rb +54 -42
  29. data/app/models/message_train/box.rb +2 -2
  30. data/app/models/message_train/conversation.rb +28 -2
  31. data/app/models/message_train/ignore.rb +2 -2
  32. data/app/models/message_train/message.rb +10 -4
  33. data/app/models/message_train/receipt.rb +27 -4
  34. data/app/models/message_train/unsubscribe.rb +1 -1
  35. data/app/views/application/404.html.haml +1 -3
  36. data/app/views/kaminari/_first_page.html.haml +1 -1
  37. data/app/views/kaminari/_last_page.html.haml +1 -1
  38. data/app/views/kaminari/_next_page.html.haml +1 -1
  39. data/app/views/kaminari/_page.html.haml +3 -3
  40. data/app/views/kaminari/_paginator.html.haml +12 -11
  41. data/app/views/kaminari/_prev_page.html.haml +1 -1
  42. data/app/views/message_train/application/_attachment_fields.html.haml +10 -7
  43. data/app/views/message_train/application/_attachment_link.html.haml +2 -2
  44. data/app/views/message_train/application/_widget.html.haml +2 -4
  45. data/app/views/message_train/boxes/_widget.html.haml +5 -5
  46. data/app/views/message_train/boxes/show.html.haml +28 -45
  47. data/app/views/message_train/collectives/_widget.html.haml +4 -4
  48. data/app/views/message_train/conversations/_conversation.html.haml +11 -11
  49. data/app/views/message_train/conversations/_deleted_toggle.html.haml +1 -1
  50. data/app/views/message_train/conversations/_ignored_toggle.html.haml +2 -2
  51. data/app/views/message_train/conversations/_read_toggle.html.haml +2 -2
  52. data/app/views/message_train/conversations/_toggle.html.haml +6 -4
  53. data/app/views/message_train/conversations/_trashed_toggle.html.haml +2 -2
  54. data/app/views/message_train/conversations/show.html.haml +8 -6
  55. data/app/views/message_train/messages/_deleted_toggle.html.haml +1 -1
  56. data/app/views/message_train/messages/_form.html.haml +22 -30
  57. data/app/views/message_train/messages/_message.html.haml +27 -45
  58. data/app/views/message_train/messages/_read_toggle.html.haml +2 -2
  59. data/app/views/message_train/messages/_toggle.html.haml +5 -1
  60. data/app/views/message_train/messages/_trashed_toggle.html.haml +2 -2
  61. data/app/views/message_train/unsubscribes/index.html.haml +12 -15
  62. data/badges/coverage.json +1 -0
  63. data/config/importmap.rb +2 -0
  64. data/config/locales/en.yml +6 -3
  65. data/db/migrate/20150721145319_create_message_train_conversations.rb +1 -1
  66. data/db/migrate/20150721160322_create_message_train_messages.rb +1 -1
  67. data/db/migrate/20150721161144_create_message_train_attachments.rb +5 -2
  68. data/db/migrate/20150721161940_create_message_train_receipts.rb +1 -1
  69. data/db/migrate/20150721163838_create_message_train_ignores.rb +1 -1
  70. data/db/migrate/20150901183458_add_received_through_to_message_train_receipts.rb +1 -1
  71. data/db/migrate/20151004184347_add_unique_index_to_receipts.rb +1 -1
  72. data/db/migrate/20151124000820_create_message_train_unsubscribes.rb +1 -1
  73. data/db/migrate/20260827095938_remove_paperclip_columns_from_message_train_attachments.rb +8 -0
  74. data/db/migrate/20260827171549_remove_body_from_message_train_messages.rb +5 -0
  75. data/db/migrate/20260827180000_add_marking_indexes.rb +9 -0
  76. data/db/migrate/20260827200000_add_body_back_to_message_train_messages.rb +5 -0
  77. data/docs/screenshots/inbox-desktop.png +0 -0
  78. data/docs/screenshots/inbox-mobile.png +0 -0
  79. data/lib/generators/message_train/install/install_generator.rb +1 -1
  80. data/lib/message_train/engine.rb +11 -0
  81. data/lib/message_train/instance_methods.rb +13 -7
  82. data/lib/message_train/localization.rb +4 -3
  83. data/lib/message_train/version.rb +1 -1
  84. data/lib/message_train.rb +0 -2
  85. data/message_train.gemspec +57 -389
  86. data/spec/controllers/message_train/boxes_controller_spec.rb +13 -8
  87. data/spec/controllers/message_train/concerns_spec.rb +2 -2
  88. data/spec/controllers/message_train/conversations_controller_spec.rb +36 -7
  89. data/spec/controllers/message_train/messages_controller_spec.rb +4 -4
  90. data/spec/dummy/app/assets/stylesheets/application.scss +141 -12
  91. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  92. data/spec/dummy/app/javascript/application.js +4 -0
  93. data/spec/dummy/app/javascript/controllers/application.js +9 -0
  94. data/spec/dummy/app/javascript/controllers/index.js +4 -0
  95. data/spec/dummy/app/models/role.rb +1 -1
  96. data/spec/dummy/app/views/active_storage/blobs/_blob.html.erb +14 -0
  97. data/spec/dummy/app/views/layouts/_top_navigation.html.haml +18 -7
  98. data/spec/dummy/app/views/layouts/application.html.haml +22 -26
  99. data/spec/dummy/bin/importmap +4 -0
  100. data/spec/dummy/config/application.rb +7 -20
  101. data/spec/dummy/config/environments/development.rb +17 -21
  102. data/spec/dummy/config/environments/production.rb +11 -63
  103. data/spec/dummy/config/environments/test.rb +21 -16
  104. data/spec/dummy/config/importmap.rb +7 -0
  105. data/spec/dummy/config/initializers/assets.rb +1 -6
  106. data/spec/dummy/config/routes.rb +1 -1
  107. data/spec/dummy/config/storage.yml +7 -0
  108. data/spec/dummy/db/migrate/20150721140013_devise_create_users.rb +1 -1
  109. data/spec/dummy/db/migrate/20150721141009_rolify_create_roles.rb +1 -1
  110. data/spec/dummy/db/migrate/20150721141128_create_groups.rb +1 -1
  111. data/spec/dummy/db/migrate/20150721150307_add_display_name_column_to_users.rb +1 -1
  112. data/spec/dummy/db/migrate/20160207190402_create_message_train_conversations.message_train.rb +1 -1
  113. data/spec/dummy/db/migrate/20160207190403_create_message_train_messages.message_train.rb +1 -1
  114. data/spec/dummy/db/migrate/20160207190404_create_message_train_attachments.message_train.rb +5 -2
  115. data/spec/dummy/db/migrate/20160207190405_create_message_train_receipts.message_train.rb +1 -1
  116. data/spec/dummy/db/migrate/20160207190406_create_message_train_ignores.message_train.rb +1 -1
  117. data/spec/dummy/db/migrate/20160207190407_add_received_through_to_message_train_receipts.message_train.rb +1 -1
  118. data/spec/dummy/db/migrate/20160207190408_add_unique_index_to_receipts.message_train.rb +1 -1
  119. data/spec/dummy/db/migrate/20160207190409_create_message_train_unsubscribes.message_train.rb +1 -1
  120. data/spec/dummy/db/migrate/20260827095939_remove_paperclip_columns_from_message_train_attachments.message_train.rb +9 -0
  121. data/spec/dummy/db/migrate/20260827161141_create_active_storage_tables.active_storage.rb +57 -0
  122. data/spec/dummy/db/migrate/20260827171550_remove_body_from_message_train_messages.message_train.rb +6 -0
  123. data/spec/dummy/db/migrate/20260827225116_add_marking_indexes.message_train.rb +10 -0
  124. data/spec/dummy/db/migrate/20260828034830_add_body_back_to_message_train_messages.message_train.rb +6 -0
  125. data/spec/dummy/db/migrate/20260828040000_drop_action_text_tables.rb +5 -0
  126. data/spec/dummy/db/schema.rb +106 -72
  127. data/spec/dummy/db/seeds/conversations.seeds.rb +22 -22
  128. data/spec/dummy/db/seeds/development/conversations.seeds.rb +2 -2
  129. data/spec/dummy/db/seeds/groups.seeds.rb +4 -4
  130. data/spec/dummy/db/seeds/test/attachments.seeds.rb +4 -4
  131. data/spec/dummy/db/seeds/test/conversations.seeds.rb +2 -2
  132. data/spec/dummy/db/seeds/users.seeds.rb +8 -8
  133. data/spec/dummy/vendor/javascript/.keep +0 -0
  134. data/spec/factories/attachment.rb +5 -6
  135. data/spec/factories/group.rb +2 -2
  136. data/spec/factories/message.rb +7 -7
  137. data/spec/factories/user.rb +3 -3
  138. data/spec/features/boxes_spec.rb +38 -34
  139. data/spec/features/conversations_spec.rb +13 -6
  140. data/spec/features/messages_spec.rb +20 -20
  141. data/spec/features/unsubscribes_spec.rb +9 -9
  142. data/spec/helpers/message_train/application_helper_spec.rb +16 -18
  143. data/spec/helpers/message_train/attachment_helper_spec.rb +8 -18
  144. data/spec/helpers/message_train/boxes_helper_spec.rb +6 -6
  145. data/spec/helpers/message_train/collectives_helper_spec.rb +10 -10
  146. data/spec/helpers/message_train/conversations_helper_spec.rb +43 -85
  147. data/spec/helpers/message_train/messages_helper_spec.rb +41 -61
  148. data/spec/models/message_train/attachment_spec.rb +26 -36
  149. data/spec/models/message_train/box_spec.rb +2 -2
  150. data/spec/models/message_train/conversation_spec.rb +15 -0
  151. data/spec/models/message_train/message_spec.rb +5 -1
  152. data/spec/models/message_train/receipt_spec.rb +1 -1
  153. data/spec/models/message_train/unsubscribe_spec.rb +1 -1
  154. data/spec/rails_helper.rb +14 -13
  155. data/spec/support/controller_behaviors.rb +13 -13
  156. data/spec/support/factory_bot.rb +3 -0
  157. data/spec/support/feature_behaviors.rb +14 -14
  158. data/spec/support/loaded_site/attachments.rb +6 -2
  159. data/spec/support/utilities.rb +49 -20
  160. metadata +260 -189
  161. data/.travis.yml +0 -12
  162. data/VERSION +0 -1
  163. data/app/assets/javascripts/ckeditor/config.js +0 -49
  164. data/app/assets/javascripts/message_train.js +0 -149
  165. data/spec/dummy/app/assets/javascripts/application.js +0 -17
  166. data/spec/dummy/app/assets/stylesheets/bootstrap-everything.scss +0 -54
  167. data/spec/dummy/config/initializers/bootstrap_leather.rb +0 -7
  168. data/spec/dummy/config/initializers/paperclip.rb +0 -5
  169. data/spec/dummy/config/secrets.yml +0 -22
  170. data/spec/dummy/db/test.sqlite3 +0 -0
  171. data/spec/support/factory_girl.rb +0 -3
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.3
1
+ 4.0.6
data/.simplecov CHANGED
@@ -1,8 +1,6 @@
1
1
  require 'simplecov'
2
- require 'coveralls'
3
2
 
4
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
5
3
  SimpleCov.start do
6
- add_filter 'lib/message_train/localization.rb'
7
- add_filter 'spec'
4
+ add_filter 'lib/message_train/localization.rb'
5
+ add_filter '/spec/'
8
6
  end
data/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ **Breaking change.** This is a from-the-ground-up modernization: Rails
6
+ 4/5 support is dropped in favor of Rails >= 7.1, < 9, and several
7
+ dependencies that no longer exist or no longer matter are removed.
8
+ There is no automated migration path from a 0.7.x installation with
9
+ real data — see "Upgrading to 1.0.0" in the README before adopting this
10
+ version.
11
+
12
+ ### Breaking
13
+
14
+ - **Ruby >= 3.2, Rails >= 7.1, < 9** required (was Rails 4/5).
15
+ - **Paperclip replaced with ActiveStorage.** `MessageTrain::Attachment`
16
+ now uses `has_one_attached :attachment` with named `:thumb`/`:large`
17
+ variants instead of Paperclip styles. Host apps must run
18
+ `bin/rails active_storage:install` (if not already using
19
+ ActiveStorage) and `bin/rails db:migrate` before upgrading. **There is
20
+ no backfill for existing Paperclip-stored files** - this version
21
+ assumes a fresh attachments table. If you have production attachment
22
+ data on 0.7.x, either stay on 0.7.x or write your own backfill before
23
+ upgrading (walk `public/system/...`, attach each file via
24
+ `ActiveStorage::Attached::One#attach`, then run the paperclip-column
25
+ removal migration).
26
+ - **CKEditor replaced with plain markdown, not Action Text.** Action
27
+ Text was evaluated and dropped in favor of markdown: message_train's
28
+ file attachments are already a separate `Attachment` model/list, not
29
+ embedded inline in the body, so Action Text's real differentiator
30
+ (drag-and-drop embeds baked into rich text) had no use here - it would
31
+ only have been a fancier textarea, at the cost of a real dependency
32
+ (Trix JS, `action_text_rich_texts` table, ActiveStorage's `main_app`
33
+ route-proxy requirement). `Message#body` is a plain `text` column,
34
+ rendered with `redcarpet` and sanitized through Rails' own `sanitize`
35
+ helper. No `action_text:install` step, no `has_rich_text`.
36
+ - **Bootstrap and jQuery removed entirely** - not just the app-specific
37
+ vendor JS (cocoon, typeahead, tokenfield), but `bootstrap-sass`,
38
+ `bootstrap_form`, `bootstrap_leather`, and `jquery-rails` themselves.
39
+ Views are now semantic HTML styled by `message_train.scss` (still a
40
+ real stylesheet, now vanilla CSS) with Turbo Frames and hand-written
41
+ Stimulus controllers. If your host app's layout/theme assumed
42
+ MessageTrain's views were Bootstrap markup, expect visual changes.
43
+ - **`importmap-rails`, `turbo-rails`, `stimulus-rails` are now runtime
44
+ dependencies.** The engine ships its own Stimulus controllers and
45
+ registers its own importmap pins and asset paths automatically via an
46
+ engine initializer - no manual pin-in wiring needed in the host app,
47
+ but the host app does need those three gems' own install generators
48
+ already run (i.e. be a normal Hotwire-based Rails 7+ app).
49
+ - **Propshaft/`dartsass-rails` replace Sprockets.** If your host app is
50
+ still on Sprockets, either migrate it to Propshaft or vendor
51
+ `message_train.scss` yourself.
52
+ - Dead `'Fixnum'` keys in `Box::MARK_METHODS` and
53
+ `Ignore::(UN)IGNORE_METHODS` fixed to `'Integer'` - these had been
54
+ silently no-oping since Ruby 2.4 on any installation using bare
55
+ integer IDs to mark/ignore.
56
+ - **CSS classes are now scoped/prefixed** (`.icon` -> `.message-train-icon`,
57
+ `.badge` -> `.message-train-badge`, and similarly for
58
+ `.attachment-thumbnails`, `.box-actions`, `.conversation-actions`,
59
+ `.date-column`, `.btn-compose`, `.dropdown-nav-item`, `.message-actions`)
60
+ so a host app's own broad selectors (e.g. a bare `.badge` or `ul`
61
+ rule) can't reach into the engine's markup and override its styling.
62
+ Classes with no engine-defined rule (`btn`, `dropdown-menu`, etc. -
63
+ now `.message-train-button` where the engine does style them) were
64
+ left alone. If your host app styled MessageTrain by targeting the
65
+ old unprefixed class names, update those selectors.
66
+ - **The install generator's example mount point changed from `/` to
67
+ `/messages`** (`mount MessageTrain::Engine => '/messages', as:
68
+ 'message_train'`) - mounting an engine at the host app's root is
69
+ unusually collision-prone. This only affects fresh installs using
70
+ the generator's suggested route; if you already mounted at a
71
+ specific path, nothing changes for you.
72
+ - **The box bulk-actions toolbar was redesigned**: the standalone
73
+ "Check All" checkbox is gone (the "Check Some" group's "All" button
74
+ does the same thing); the "Mark" actions are always-visible icon+text
75
+ buttons instead of hidden behind a dropdown. If your host app
76
+ customized this toolbar's markup or CSS, expect to redo that
77
+ customization.
78
+
79
+ ### Fixed
80
+
81
+ - `Message#create_conversation_if_blank` now runs `before_validation`
82
+ instead of `before_create`, fixing conversation-presence validation
83
+ under Rails 5+'s default-required `belongs_to`.
84
+ - `Role#resource`, `Unsubscribe#from`, `Receipt#received_through` marked
85
+ `optional: true` where they're intentionally nullable (global roles,
86
+ unsubscribe-from-everything, direct receipts), matching Rails 5+'s
87
+ default-required `belongs_to`.
88
+ - `Receipt.mark`/`Message.mark` now bulk-update instead of looping a
89
+ per-row save, while still touching the affected messages/conversations
90
+ so inbox ordering and fragment-cache keys (which depend on
91
+ `Conversation#updated_at` bumping on mark) keep working.
92
+ - Several N+1 query sites addressed: box/conversation listing now
93
+ preloads what its views actually walk per row, `Box#unread_count` and
94
+ `collective_boxes` are memoized per-instance, and
95
+ `Conversation#participant_ignored?` filters in-memory instead of
96
+ querying on every call. (`Conversation#includes_*_for?`'s receipt-flag
97
+ N+1 is a known, documented remaining case - see the comment at
98
+ `Conversation#includes_matching_receipts?`.)
99
+ - New indexes on `message_train_receipts.marked_read/marked_trash/
100
+ marked_deleted`, `message_train_conversations.updated_at`, and
101
+ `message_train_messages.draft`.
102
+ - `Conversation#new_reply` no longer builds the reply body as
103
+ `<blockquote>...</blockquote>` HTML, stale from the Action Text era -
104
+ since `Message#body` is plain markdown text now, replies were being
105
+ pre-filled with literal HTML tags as visible text. Replies now quote
106
+ the previous message as proper markdown (`> ` per line).
107
+ - Attaching a file and submitting a message that then fails validation
108
+ (e.g. a blank subject) no longer crashes on re-render. The nested
109
+ attachment-fields partial checked `attachment.attached?`, which is
110
+ true as soon as a file is chosen in-memory even before its
111
+ ActiveStorage blob is persisted; it now also checks
112
+ `attachment.blob.persisted?` before building a thumbnail/link URL.
113
+ - The conversations listing table (box/collective show pages) now
114
+ actually fills its container (`width: 100%`, real cell padding/
115
+ borders) instead of shrinking to its content width.
116
+
117
+ ### Tooling (shouldn't affect host apps)
118
+
119
+ - Juwelier replaced with a hand-written gemspec; `Rakefile` no longer
120
+ depends on it.
121
+ - Travis CI replaced with GitHub Actions.
122
+ - factory_girl -> factory_bot, poltergeist -> Cuprite, coveralls dropped,
123
+ `should` -> `expect` syntax throughout the spec suite, rubocop bumped
124
+ with rubocop-rails/rubocop-rspec.
125
+
126
+ ## 0.7.6 and earlier
127
+
128
+ See git history - no changelog was kept before 1.0.0.
data/CLAUDE.md ADDED
@@ -0,0 +1,5 @@
1
+ # message_train
2
+
3
+ ## Client
4
+
5
+ Fiendish Labs, via GemVein (owns all gems, owned by Fiendish Labs). Fiendish Labs (Calendula and Martusjaav's household company). See `Office/projects/TYPES.md`'s Ownership section for the full taxonomy.
data/Gemfile CHANGED
@@ -1,58 +1,56 @@
1
1
  source 'https://rubygems.org'
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
- gem 'haml-rails', '>= 1', '< 2'
5
- gem 'jquery-rails', '~> 4'
6
- gem 'paperclip', '>= 4.2', '< 6'
7
- gem 'rails', '>= 4', '< 6'
8
- gem 'rails-i18n', '>= 4', '< 6'
9
- gem 'uglifier', '>= 2.7', '< 4'
4
+ gem 'haml-rails', '>= 2'
5
+ gem 'rails', '>= 7.1', '< 9'
6
+ gem 'rails-i18n', '>= 7', '< 9'
7
+ gem 'image_processing', '~> 1.2'
8
+ gem 'mini_magick', '~> 4.12'
9
+ gem 'benchmark' # mini_magick requires it; no longer a Ruby default gem as of 4.0
10
10
 
11
11
  # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
12
12
  gem 'jbuilder', '~> 2.0'
13
13
 
14
- gem 'bootstrap-sass', '~> 3.3'
15
- gem 'bootstrap_form', '~> 2.3'
16
- gem 'bootstrap_leather', '>= 0.10.11', '< 1'
14
+ gem 'dartsass-rails', '~> 0.5'
15
+ gem 'importmap-rails', '~> 2.0'
17
16
  gem 'kaminari', '~> 1.0'
18
- gem 'bootstrap_tokenfield_rails', '~> 0.12'
19
- gem 'ckeditor'
20
- gem 'cocoon', '~> 1.2'
21
- gem 'jquery-ui-bootstrap-rails'
22
- gem 'sass-rails', '~> 5'
23
- gem 'twitter-typeahead-rails'
17
+ gem 'propshaft', '~> 1.1'
18
+ gem 'redcarpet', '~> 3.6'
19
+ gem 'stimulus-rails', '~> 1.3'
20
+ gem 'turbo-rails', '~> 2.0'
24
21
 
25
22
  # Add dependencies to develop your gem here.
26
23
  # Include everything needed to run rake, tests, features, etc.
27
24
  group :development do
28
- gem 'bundler', '~> 1.0'
29
- gem 'juwelier', '~> 2'
30
- gem 'rubocop', '~> 0.47.1', require: false
31
- gem 'scss_lint', '~> 0.52', require: false
25
+ gem 'rubocop', '~> 1.6', require: false
26
+ gem 'rubocop-rails', '~> 2.25', require: false
27
+ gem 'rubocop-rake', '~> 0.7', require: false
28
+ gem 'rubocop-rspec', '~> 3.0', require: false
32
29
  end
33
30
 
34
31
  group :development, :test do
35
- gem 'byebug', '~> 9'
32
+ gem 'byebug', '~> 11'
36
33
  gem 'devise', '~> 4'
37
- gem 'factory_girl_rails', '~> 4.5'
38
- gem 'faker', '~> 1.4'
34
+ gem 'factory_bot_rails', '~> 6.4'
35
+ gem 'faker', '~> 3'
39
36
  gem 'friendly_id', '~> 5'
40
37
  gem 'high_voltage', '~> 3'
41
- gem 'rolify', '>= 4', '< 5.2'
42
- gem 'rspec-its', '>= 1'
43
- gem 'rspec-rails', '>= 3', '< 4'
38
+ gem 'rolify', '>= 4'
39
+ gem 'rspec-its', '>= 1', '< 2'
40
+ gem 'rspec-rails', '~> 7.0'
44
41
  gem 'seedbank', '~> 0.3'
45
- gem 'sqlite3', '~> 1.3'
42
+ gem 'sqlite3', '~> 2.1'
46
43
  end
47
44
 
48
45
  group :test do
49
- gem 'capybara', '~> 2.4'
50
- gem 'coveralls', '~> 0.8', require: false
51
- gem 'database_cleaner', '~> 1.4'
46
+ gem 'capybara', '~> 3.40'
47
+ gem 'cuprite', '~> 0.15'
48
+ gem 'database_cleaner-active_record', '~> 2.1'
52
49
  gem 'launchy', '~> 2', require: false
53
- gem 'poltergeist', '~> 1.14'
50
+ gem 'puma', '~> 6.0'
54
51
  gem 'rails-controller-testing', '~> 1.0'
55
52
  gem 'rspec-collection_matchers', '>= 1'
56
- gem 'rspec-html-matchers', '~> 0.7'
57
- gem 'shoulda-matchers', '>= 2.8', '< 3.2'
53
+ gem 'rspec-html-matchers', '~> 0.10'
54
+ gem 'shoulda-matchers', '~> 6.0'
55
+ gem 'simplecov', '~> 0.22', require: false
58
56
  end
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Gem Vein
1
+ Copyright (c) 2015-2026 Gem Vein
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,16 +1,30 @@
1
1
  # MessageTrain
2
- [![Build Status](https://travis-ci.org/gemvein/message_train.svg)](https://travis-ci.org/gemvein/message_train)
3
- [![Coverage Status](https://coveralls.io/repos/gemvein/message_train/badge.svg?branch=master&service=github)](https://coveralls.io/github/gemvein/message_train?branch=master)
4
- [![Gem Version](https://badge.fury.io/rb/message_train.svg)](http://badge.fury.io/rb/message_train)
5
-
6
- MessageTrain is a Rails 4 and 5 Private Messaging Gem that uses bootstrap to send
7
- and display private messages from one user to another. It can also be
8
- configured to send messages to a user collective (such as a certain Role or
9
- Group of users).
2
+ [![CI](https://github.com/gemvein/message_train/actions/workflows/ci.yml/badge.svg)](https://github.com/gemvein/message_train/actions/workflows/ci.yml)
3
+ [![Gem Version](https://img.shields.io/gem/v/message_train)](https://rubygems.org/gems/message_train)
4
+ [![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/gemvein/message_train/HEAD/badges/coverage.json)](https://github.com/gemvein/message_train/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](http://opensource.org/licenses/MIT)
6
+
7
+ <!--
8
+ MessageTrain ships real views/CSS/JS, so it's a candidate for these once
9
+ each has a real automated check backing it - add whichever apply as that
10
+ check is actually wired up and passing. Don't add a badge for a check
11
+ that isn't actually running; an unbacked badge is worse than none. None
12
+ of these checks exist yet as of 2026-08-28.
13
+
14
+ [![Semantic HTML](https://img.shields.io/badge/html-semantic-blueviolet)](#)
15
+ [![CSS: clean](https://img.shields.io/badge/css-clean-blueviolet)](#)
16
+ [![JS: vanilla & minimal](https://img.shields.io/badge/js-vanilla%20%26%20minimal-blueviolet)](#)
17
+ [![Accessibility: checked](https://img.shields.io/badge/a11y-checked-blueviolet)](#)
18
+ -->
19
+
20
+ MessageTrain is a Rails Engine for private messaging, built on Turbo
21
+ Frames and Stimulus, that lets users send and display private messages
22
+ to one another. It can also be configured to send messages to a user
23
+ collective (such as a certain Role or Group of users).
10
24
 
11
25
  Messages can be saved as drafts instead of sending. Message composition
12
- features type-ahead completion for recipients, wysiwyg html bodies, and an
13
- arbitrary number of attachments. Messages are grouped together into
26
+ features type-ahead completion for recipients, markdown-formatted bodies,
27
+ and an arbitrary number of attachments. Messages are grouped together into
14
28
  conversations, and allow valid senders to reply to a given message. Any given
15
29
  conversation can be ignored if it is no longer of interest to the user, at
16
30
  which point no further messages will be received in that conversation. The
@@ -26,24 +40,47 @@ through a collective (unless they have unsubscribed from those notifications
26
40
  or all notifications).
27
41
 
28
42
  ### Inbox Page
29
- ![Inbox Screenshot](http://gemvein.com/assets/screenshots/message_train-box-in.png)
43
+ <p>
44
+ <img src="docs/screenshots/inbox-desktop.png" alt="Inbox on desktop" width="70%">
45
+ <img src="docs/screenshots/inbox-mobile.png" alt="Inbox on mobile" width="25%">
46
+ </p>
30
47
 
31
48
  ## Installation
32
49
 
33
- First, install the gem:
34
-
50
+ MessageTrain is a normal Hotwire-based Rails 7.1+ app dependency - it
51
+ expects your host app already has `importmap-rails`, `turbo-rails`, and
52
+ `stimulus-rails` installed (their own install generators already run),
53
+ and is on Propshaft rather than Sprockets. It uses ActiveStorage for
54
+ attachments, so run its installer too if you haven't already:
55
+
35
56
  ```bash
36
- gem 'ckeditor'
37
- gem 'message_train'
57
+ bin/rails active_storage:install
38
58
  ```
39
59
 
40
- Then, run the install generator:
60
+ **Watch the file-conflict prompt carefully** if your app has customized
61
+ `spec/spec_helper.rb` or `spec/rails_helper.rb` - answering (or missing)
62
+ the overwrite prompt wrong can silently replace your project's own spec
63
+ config. Diff after running the installer if you're not sure what
64
+ changed.
65
+
66
+ Message bodies are plain markdown text, rendered with `redcarpet` and
67
+ sanitized through Rails' own `sanitize` helper - no Action
68
+ Text/Trix dependency, and no separate install step needed for it.
69
+
70
+ Then add the gem and run the install generator:
71
+
72
+ ```ruby
73
+ gem 'message_train'
74
+ ```
41
75
 
42
76
  ```bash
43
77
  rails g message_train:install
78
+ bin/rails db:migrate
44
79
  ```
45
80
 
46
- And then run `rake db:migrate`.
81
+ The engine registers its own Stimulus controllers and importmap pins
82
+ automatically (via an engine initializer) - no manual pin-in wiring
83
+ needed in the host app.
47
84
 
48
85
  Next, add to your models, each of which will need some kind of display
49
86
  name column and some kind of slug (could be the same). See below for the
@@ -62,35 +99,30 @@ To include Message Train variables and helpers in your controllers, add
62
99
  this concern to your controller or application controller:
63
100
 
64
101
  ```ruby
65
- include MessageTrain::MessageTrainSupport
102
+ include MessageTrainSupport
66
103
  ```
67
104
 
68
- Add to your application.css.scss:
105
+ Add to your application.css (or application.scss, if you're using
106
+ dartsass-rails as message_train itself does):
69
107
 
70
108
  ```scss
71
109
  @import 'message_train';
72
110
  ```
73
111
 
74
- And in your application.js:
75
-
76
- ```js
77
- //= require message_train
78
- ```
79
-
80
112
  In your layout, supposing you use haml:
81
113
 
82
114
  ```haml
83
115
  #alert_area
84
- = alert_flash_messages
116
+ - flash.each do |type, message|
117
+ .alert{ class: flash_alert_class(type) }= message
85
118
  ```
86
119
 
87
- If you use bootstrap, you can use the built-in bootstrap sidebar menu
88
- (makes use of bootstrap_leather, which is a dependency of this gem)
120
+ If you'd like the built-in boxes dropdown/widget for navigation:
89
121
 
90
122
  ```haml
91
123
  - if user_signed_in?
124
+ = boxes_dropdown_list(current_user)
92
125
  = message_train_widget
93
- = render_widgets 'md', 3
94
126
  ```
95
127
 
96
128
  ### Required helper methods
@@ -155,13 +187,13 @@ config.address_book_methods[:users] = :valid_recipients_for
155
187
  ### Boxes
156
188
  <dl>
157
189
  <dt>boxes_dropdown_list</dt>
158
- <dd>Bootstrap navigation dropdown list of boxes. (Be sure to check thatuser is signed in before calling, or you'll get errors.)</dd>
190
+ <dd>Navigation dropdown list of boxes. (Be sure to check that user is signed in before calling, or you'll get errors.)</dd>
159
191
  <dt>boxes_widget</dt>
160
- <dd>Bootstrap widget with list of boxes</dd>
192
+ <dd>Widget with list of boxes</dd>
161
193
  <dt>box_nav_item(box)</dt>
162
- <dd>Bootstrap list item for one box</dd>
194
+ <dd>List item for one box</dd>
163
195
  <dt>box_list_item(box)</dt>
164
- <dd>Bootstrap list item for one box</dd>
196
+ <dd>List item for one box</dd>
165
197
  <dt>box_participant_name(participant)</dt>
166
198
  <dd>Name of the participant, according to the method specified in yourconfiguration or model.</dd>
167
199
  <dt>box_participant_slug(participant)</dt>
@@ -237,6 +269,45 @@ config.address_book_methods[:users] = :valid_recipients_for
237
269
 
238
270
  ## Upgrading
239
271
 
272
+ ### 1.0.0
273
+
274
+ This is a breaking change - see CHANGELOG.md for the full list. The
275
+ short version:
276
+
277
+ * Rails must be >= 7.1, < 9, and Ruby >= 3.2.
278
+ * Run `bin/rails active_storage:install` (if you haven't already) before
279
+ running MessageTrain's own migrations. **There is no automated
280
+ backfill for existing Paperclip-stored attachment data** - this
281
+ version assumes a fresh attachments table. If you have real attachment
282
+ data on 0.7.x in production, stay on 0.7.x until you've written your
283
+ own backfill (walk your existing `public/system/...` files, attach
284
+ each one via `ActiveStorage::Attached::One#attach`), or accept
285
+ starting fresh.
286
+ * `Message#body` is now a plain markdown text column instead of a
287
+ CKEditor-edited HTML column - rendered with `redcarpet` and sanitized
288
+ through Rails' `sanitize` helper. If you were on a pre-1.0 build of
289
+ this branch that used Action Text, there's no automated conversion
290
+ from stored rich-text HTML to markdown source; either write your own
291
+ conversion or start fresh.
292
+ * Bootstrap and jQuery are gone. If your host app's own layout/CSS
293
+ assumed MessageTrain's views were Bootstrap markup, expect visual
294
+ changes - views are now semantic HTML you can restyle directly.
295
+ * Your host app needs `importmap-rails`, `turbo-rails`, and
296
+ `stimulus-rails` already installed (a normal Rails 7+ Hotwire app) and
297
+ Propshaft instead of Sprockets.
298
+ * `alert_flash_messages` (a `bootstrap_leather` helper) no longer
299
+ exists - render `flash` yourself in `#alert_area`, using the new
300
+ `flash_alert_class(type)` helper to map Rails' flash keys to CSS
301
+ classes. See the Installation section above.
302
+ * MessageTrain's CSS classes are now prefixed (`.icon` ->
303
+ `.message-train-icon`, `.badge` -> `.message-train-badge`, etc.) so
304
+ the engine's markup can't be reached by a host app's own broad
305
+ selectors. If you styled MessageTrain by targeting the old
306
+ unprefixed class names, update those selectors.
307
+ * The install generator's example route now mounts at `/messages`
308
+ instead of `/` - only relevant if you re-run the generator or are
309
+ copying its suggested route for a fresh install.
310
+
240
311
  ### 0.7.1
241
312
 
242
313
  Renamed `subscription` to `message_train_subscription` and `subscriptions` to `message_train_subscription`.
@@ -280,9 +351,9 @@ they will simply be skipped.
280
351
  it.
281
352
 
282
353
  ## Contributors
283
- * [Karen Lundgren](https://github.com/nerakdon)
354
+ * [Loren Lundgren](https://github.com/nerakdon)
284
355
  * [Chad Lundgren](https://github.com/chadlundgren)
285
356
 
286
357
  ## Copyright
287
358
 
288
- Copyright (c) 2015-2017 Gem Vein. See LICENSE.txt for further details.
359
+ Copyright (c) 2015-2026 Gem Vein. See LICENSE.txt for further details.
data/Rakefile CHANGED
@@ -8,23 +8,9 @@ rescue Bundler::BundlerError => e
8
8
  $stderr.puts 'Run `bundle install` to install missing gems'
9
9
  exit e.status_code
10
10
  end
11
+ require 'bundler/gem_tasks'
11
12
  require 'rake'
12
13
 
13
- require 'juwelier'
14
- Juwelier::Tasks.new do |gem|
15
- # gem is a Gem::Specification...
16
- # see http://guides.rubygems.org/specification-reference/ for more options
17
- gem.name = 'message_train'
18
- gem.homepage = 'http://www.gemvein.com/museum/cases/message_train'
19
- gem.license = 'MIT'
20
- gem.summary = 'Rails 4 & 5 Engine providing messaging for any object'
21
- gem.description = 'Rails 4 & 5 Engine providing private and public messaging'\
22
- ' for any object, such as Users or Groups'
23
- gem.email = 'karen.e.lundgren@gmail.com'
24
- gem.authors = ['Karen Lundgren']
25
- end
26
- Juwelier::RubygemsDotOrgTasks.new
27
-
28
14
  require 'rspec/core'
29
15
  require 'rspec/core/rake_task'
30
16
  RSpec::Core::RakeTask.new(:spec) do |spec|
@@ -40,7 +26,7 @@ end
40
26
  task default: :spec
41
27
 
42
28
  require 'rdoc/task'
43
- Rake::RDocTask.new do |rdoc|
29
+ RDoc::Task.new do |rdoc|
44
30
  version = File.exist?('VERSION') ? File.read('VERSION') : ''
45
31
 
46
32
  rdoc.rdoc_dir = 'rdoc'
@@ -55,9 +41,9 @@ load 'rails/tasks/engine.rake'
55
41
  # load 'rails/tasks/statistics.rake'
56
42
 
57
43
  namespace :message_train do
58
- desc 'Clean out development system files'
44
+ desc 'Clean out development ActiveStorage files'
59
45
  task files: :environment do
60
- FileUtils.rm_rf(Dir["#{Rails.root}/public/system/development/*/*"])
46
+ FileUtils.rm_rf(Dir["#{Rails.root}/storage/*"])
61
47
  end
62
48
  desc 'Recreate database from seeds and clean out all system files'
63
49
  task clean: :environment do