mobility-actiontext 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +18 -0
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +152 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +91 -0
  9. data/Rakefile +1 -0
  10. data/lib/mobility/action_text.rb +4 -0
  11. data/lib/mobility/action_text/version.rb +7 -0
  12. data/lib/mobility/backends/action_text.rb +98 -0
  13. data/mobility-actiontext.gemspec +33 -0
  14. data/test_app/.gitignore +40 -0
  15. data/test_app/Gemfile +50 -0
  16. data/test_app/Gemfile.lock +221 -0
  17. data/test_app/Rakefile +6 -0
  18. data/test_app/app/assets/config/manifest.js +2 -0
  19. data/test_app/app/assets/stylesheets/application.css +15 -0
  20. data/test_app/app/controllers/application_controller.rb +2 -0
  21. data/test_app/app/helpers/application_helper.rb +2 -0
  22. data/test_app/app/models/application_record.rb +3 -0
  23. data/test_app/app/models/post.rb +7 -0
  24. data/test_app/app/views/layouts/application.html.erb +15 -0
  25. data/test_app/bin/bundle +114 -0
  26. data/test_app/bin/rails +4 -0
  27. data/test_app/bin/rake +4 -0
  28. data/test_app/bin/setup +33 -0
  29. data/test_app/config.ru +6 -0
  30. data/test_app/config/application.rb +35 -0
  31. data/test_app/config/boot.rb +3 -0
  32. data/test_app/config/credentials.yml.enc +1 -0
  33. data/test_app/config/database.yml +25 -0
  34. data/test_app/config/environment.rb +5 -0
  35. data/test_app/config/environments/development.rb +71 -0
  36. data/test_app/config/environments/production.rb +106 -0
  37. data/test_app/config/environments/test.rb +52 -0
  38. data/test_app/config/initializers/application_controller_renderer.rb +8 -0
  39. data/test_app/config/initializers/assets.rb +14 -0
  40. data/test_app/config/initializers/backtrace_silencers.rb +8 -0
  41. data/test_app/config/initializers/content_security_policy.rb +30 -0
  42. data/test_app/config/initializers/cookies_serializer.rb +5 -0
  43. data/test_app/config/initializers/filter_parameter_logging.rb +6 -0
  44. data/test_app/config/initializers/i18n.rb +3 -0
  45. data/test_app/config/initializers/inflections.rb +16 -0
  46. data/test_app/config/initializers/mime_types.rb +4 -0
  47. data/test_app/config/initializers/mobility.rb +15 -0
  48. data/test_app/config/initializers/permissions_policy.rb +11 -0
  49. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  50. data/test_app/config/locales/en.yml +33 -0
  51. data/test_app/config/puma.rb +43 -0
  52. data/test_app/config/routes.rb +3 -0
  53. data/test_app/config/storage.yml +34 -0
  54. data/test_app/db/migrate/20210114124021_create_active_storage_tables.active_storage.rb +39 -0
  55. data/test_app/db/migrate/20210114124022_create_action_text_tables.action_text.rb +16 -0
  56. data/test_app/db/migrate/20210114124951_translate_rich_texts.rb +17 -0
  57. data/test_app/db/migrate/20210114125134_create_posts.rb +7 -0
  58. data/test_app/db/schema.rb +64 -0
  59. data/test_app/db/seeds.rb +7 -0
  60. data/test_app/public/404.html +67 -0
  61. data/test_app/public/422.html +67 -0
  62. data/test_app/public/500.html +66 -0
  63. data/test_app/public/apple-touch-icon-precomposed.png +0 -0
  64. data/test_app/public/apple-touch-icon.png +0 -0
  65. data/test_app/public/favicon.ico +0 -0
  66. data/test_app/public/robots.txt +1 -0
  67. data/test_app/test/application_system_test_case.rb +5 -0
  68. data/test_app/test/fixtures/action_text/rich_texts.yml +35 -0
  69. data/test_app/test/fixtures/posts.yml +3 -0
  70. data/test_app/test/mobility_action_text_test.rb +108 -0
  71. data/test_app/test/test_helper.rb +28 -0
  72. metadata +158 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1f65c8e64ed83c88b3a6c2d8ccba46d4bf4a2503eba6d1ea4f69d51c363fa7a
4
+ data.tar.gz: 07d9ea9ecc5c9cf06b9eb2d2d0d16e952b2754f2fc0f6dcfed1aa7151d757ce3
5
+ SHA512:
6
+ metadata.gz: 778621cdbe8d1820c76e3770f74f0e2e2a1f67cadb49a39672ad2d8355cd93e4e155feb4925808f3bc1235f6c528eb43f46a5eef8de73e0fce34bb38e722690a
7
+ data.tar.gz: 0a0b1a8b171a252228669deb7bad1b409d091d999294e61edd69bf6f4366cb89965c7ac56dff6c90101d1fd76c75d74efc8110d00b776230a9b77467d993f025
@@ -0,0 +1,18 @@
1
+ name: CI
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - uses: ruby/setup-ruby@v1
11
+ - uses: actions/cache@v2
12
+ with:
13
+ path: vendor/bundle
14
+ key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }}
15
+ restore-keys: |
16
+ bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-
17
+ - run: test_app/bin/setup
18
+ - run: cd test_app && bundle exec rails test
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.0.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Mobility Action Text Changelog
2
+
3
+ ### 0.1.0
4
+ - Initial release.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,152 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mobility-actiontext (0.1.0)
5
+ mobility (~> 1.1)
6
+ rails (~> 6.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.1.3)
12
+ actionpack (= 6.1.3)
13
+ activesupport (= 6.1.3)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailbox (6.1.3)
17
+ actionpack (= 6.1.3)
18
+ activejob (= 6.1.3)
19
+ activerecord (= 6.1.3)
20
+ activestorage (= 6.1.3)
21
+ activesupport (= 6.1.3)
22
+ mail (>= 2.7.1)
23
+ actionmailer (6.1.3)
24
+ actionpack (= 6.1.3)
25
+ actionview (= 6.1.3)
26
+ activejob (= 6.1.3)
27
+ activesupport (= 6.1.3)
28
+ mail (~> 2.5, >= 2.5.4)
29
+ rails-dom-testing (~> 2.0)
30
+ actionpack (6.1.3)
31
+ actionview (= 6.1.3)
32
+ activesupport (= 6.1.3)
33
+ rack (~> 2.0, >= 2.0.9)
34
+ rack-test (>= 0.6.3)
35
+ rails-dom-testing (~> 2.0)
36
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
37
+ actiontext (6.1.3)
38
+ actionpack (= 6.1.3)
39
+ activerecord (= 6.1.3)
40
+ activestorage (= 6.1.3)
41
+ activesupport (= 6.1.3)
42
+ nokogiri (>= 1.8.5)
43
+ actionview (6.1.3)
44
+ activesupport (= 6.1.3)
45
+ builder (~> 3.1)
46
+ erubi (~> 1.4)
47
+ rails-dom-testing (~> 2.0)
48
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
49
+ activejob (6.1.3)
50
+ activesupport (= 6.1.3)
51
+ globalid (>= 0.3.6)
52
+ activemodel (6.1.3)
53
+ activesupport (= 6.1.3)
54
+ activerecord (6.1.3)
55
+ activemodel (= 6.1.3)
56
+ activesupport (= 6.1.3)
57
+ activestorage (6.1.3)
58
+ actionpack (= 6.1.3)
59
+ activejob (= 6.1.3)
60
+ activerecord (= 6.1.3)
61
+ activesupport (= 6.1.3)
62
+ marcel (~> 0.3.1)
63
+ mimemagic (~> 0.3.2)
64
+ activesupport (6.1.3)
65
+ concurrent-ruby (~> 1.0, >= 1.0.2)
66
+ i18n (>= 1.6, < 2)
67
+ minitest (>= 5.1)
68
+ tzinfo (~> 2.0)
69
+ zeitwerk (~> 2.3)
70
+ builder (3.2.4)
71
+ concurrent-ruby (1.1.8)
72
+ crass (1.0.6)
73
+ erubi (1.10.0)
74
+ globalid (0.4.2)
75
+ activesupport (>= 4.2.0)
76
+ i18n (1.8.9)
77
+ concurrent-ruby (~> 1.0)
78
+ loofah (2.9.0)
79
+ crass (~> 1.0.2)
80
+ nokogiri (>= 1.5.9)
81
+ mail (2.7.1)
82
+ mini_mime (>= 0.1.1)
83
+ marcel (0.3.3)
84
+ mimemagic (~> 0.3.2)
85
+ method_source (1.0.0)
86
+ mimemagic (0.3.5)
87
+ mini_mime (1.0.2)
88
+ minitest (5.14.3)
89
+ mobility (1.1.1)
90
+ i18n (>= 0.6.10, < 2)
91
+ request_store (~> 1.0)
92
+ nio4r (2.5.5)
93
+ nokogiri (1.11.1-x86_64-darwin)
94
+ racc (~> 1.4)
95
+ racc (1.5.2)
96
+ rack (2.2.3)
97
+ rack-test (1.1.0)
98
+ rack (>= 1.0, < 3)
99
+ rails (6.1.3)
100
+ actioncable (= 6.1.3)
101
+ actionmailbox (= 6.1.3)
102
+ actionmailer (= 6.1.3)
103
+ actionpack (= 6.1.3)
104
+ actiontext (= 6.1.3)
105
+ actionview (= 6.1.3)
106
+ activejob (= 6.1.3)
107
+ activemodel (= 6.1.3)
108
+ activerecord (= 6.1.3)
109
+ activestorage (= 6.1.3)
110
+ activesupport (= 6.1.3)
111
+ bundler (>= 1.15.0)
112
+ railties (= 6.1.3)
113
+ sprockets-rails (>= 2.0.0)
114
+ rails-dom-testing (2.0.3)
115
+ activesupport (>= 4.2.0)
116
+ nokogiri (>= 1.6)
117
+ rails-html-sanitizer (1.3.0)
118
+ loofah (~> 2.3)
119
+ railties (6.1.3)
120
+ actionpack (= 6.1.3)
121
+ activesupport (= 6.1.3)
122
+ method_source
123
+ rake (>= 0.8.7)
124
+ thor (~> 1.0)
125
+ rake (13.0.3)
126
+ request_store (1.5.0)
127
+ rack (>= 1.4)
128
+ sprockets (4.0.2)
129
+ concurrent-ruby (~> 1.0)
130
+ rack (> 1, < 3)
131
+ sprockets-rails (3.2.2)
132
+ actionpack (>= 4.0)
133
+ activesupport (>= 4.0)
134
+ sprockets (>= 3.0.0)
135
+ sqlite3 (1.4.2)
136
+ thor (1.1.0)
137
+ tzinfo (2.0.4)
138
+ concurrent-ruby (~> 1.0)
139
+ websocket-driver (0.7.3)
140
+ websocket-extensions (>= 0.1.0)
141
+ websocket-extensions (0.1.5)
142
+ zeitwerk (2.4.2)
143
+
144
+ PLATFORMS
145
+ x86_64-darwin-20
146
+
147
+ DEPENDENCIES
148
+ mobility-actiontext!
149
+ sqlite3 (~> 1.4)
150
+
151
+ BUNDLED WITH
152
+ 2.2.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Sébastien Dubois
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Mobility Action Text
2
+
3
+ ![Gem Version](https://badge.fury.io/rb/mobility-actiontext.svg)
4
+ ![Build Status](https://github.com/sedubois/mobility-actiontext/workflows/CI/badge.svg)
5
+
6
+ Translate Rails [Action Text](https://guides.rubyonrails.org/action_text_overview.html) rich text with [Mobility](https://github.com/shioyama/mobility).
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'mobility-actiontext'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install mobility-actiontext
23
+
24
+ Make sure that Action Text is [installed](https://guides.rubyonrails.org/action_text_overview.html#installation), then run this migration:
25
+
26
+ ```rb
27
+ class TranslateRichTexts < ActiveRecord::Migration[6.1]
28
+ def change
29
+ # or null: true to allow untranslated rich text
30
+ add_column :action_text_rich_texts, :locale, :string, null: false
31
+
32
+ remove_index :action_text_rich_texts,
33
+ column: [:record_type, :record_id, :name],
34
+ name: :index_action_text_rich_texts_uniqueness,
35
+ unique: true
36
+ add_index :action_text_rich_texts,
37
+ [:record_type, :record_id, :name, :locale],
38
+ name: :index_action_text_rich_texts_uniqueness,
39
+ unique: true
40
+ end
41
+ end
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ```diff
47
+ # app/models/message.rb
48
+ class Message < ApplicationRecord
49
+ + extend Mobility
50
+ +
51
+ - has_rich_text :content
52
+ + translates :content, backend: :action_text
53
+ end
54
+ ```
55
+
56
+ ### Plain text
57
+
58
+ Although the main purpose of the `action_text_rich_texts` table is to store rich text, this gem allows using it for plain text as well. This could be useful to consolidate all text in a single table and to remove the need to migrate data when converting between one and the other.
59
+
60
+ ```diff
61
+ # app/models/message.rb
62
+ class Message < ApplicationRecord
63
+ extend Mobility
64
+
65
+ + translates :title, backend: :action_text, plain: true
66
+ translates :content, backend: :action_text
67
+ end
68
+ ```
69
+
70
+ ## Implementation note
71
+
72
+ Action Text's rich text content is saved in its own model that can be associated with any existing Active Record model using a polymorphic relation. Likewise, Mobility's KeyValue backend allows to translate any model using a polymorphic relation. This gem makes use of this similarity by combining both features in a single step, thus offering rich text "for free", i.e. in theory at no extra performance cost compared to untranslated rich text or translated plain text.
73
+
74
+ This is done through the `Mobility::Backends::ActionText::Translation` model extending `ActionText::RichText`. This model is backed by Action Text's existing `action_text_rich_texts` table and its existing `name`, `body` and `record` attributes, to which is added a new `locale` attribute.
75
+
76
+ [Read more](https://github.com/shioyama/mobility/issues/385) on the genesis of this gem.
77
+
78
+ ## Development
79
+
80
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
81
+
82
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
83
+
84
+ ## Contributing
85
+
86
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sedubois/mobility-actiontext.
87
+
88
+ ## License
89
+
90
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
91
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'action_text/version'
4
+ require_relative 'backends/action_text'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mobility
4
+ module ActionText
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mobility/backends/active_record/key_value'
4
+
5
+ module Mobility
6
+ # -
7
+ module Backends
8
+ #
9
+ # Implements the {Mobility::Backends::KeyValue} backend for ActionText.
10
+ #
11
+ # @example
12
+ # class Post < ApplicationRecord
13
+ # extend Mobility
14
+ # translates :content, backend: :action_text
15
+ # end
16
+ #
17
+ # post = Post.create(content: "<h1>My text is rich</h1>")
18
+ # post.rich_text_translations
19
+ # #=> #<ActionText::RichText::ActiveRecord_Associations_CollectionProxy ... >
20
+ # post.rich_text_translations.first.to_s
21
+ # #=> "<div class=\"trix-content\">\n <h1>My text is rich</h1>\n</div>\n"
22
+ # post.content
23
+ # #=> "<div class=\"trix-content\">\n <h1>My text is rich</h1>\n</div>\n"
24
+ # post.rich_text_translations.first.class
25
+ # #=> Mobility::Backends::ActionText::RichTextTranslation
26
+ #
27
+ class ActionText < ActiveRecord::KeyValue
28
+ # override to return record instead of value
29
+ def read(locale, **options)
30
+ return super if self.options[:plain]
31
+ translation_for(locale, **options)
32
+ end
33
+
34
+ class << self
35
+ def valid_keys
36
+ super.tap { |keys| keys.delete(:type) } << :plain
37
+ end
38
+
39
+ # @!group Backend Configuration
40
+ # @option (see Mobility::Backends::KeyValue::ClassMethods#configure)
41
+ def configure(options)
42
+ options[:plain] = false unless options.has_key?(:plain)
43
+ if options[:plain]
44
+ options[:association_name] ||= 'plain_text_translations'
45
+ options[:class_name] ||= PlainTextTranslation
46
+ else
47
+ options[:association_name] ||= 'rich_text_translations'
48
+ options[:class_name] ||= RichTextTranslation
49
+ end
50
+ options[:key_column] ||= :name
51
+ options[:value_column] ||= :body
52
+ options[:belongs_to] ||= :record
53
+ super
54
+ end
55
+ # @!endgroup
56
+ end
57
+
58
+ setup do |attributes, _options|
59
+ attributes.each do |name|
60
+ has_one :"rich_text_#{name}", -> { where(name: name, locale: Mobility.locale) },
61
+ class_name: 'Mobility::Backends::ActionText::RichTextTranslation',
62
+ as: :record, inverse_of: :record, autosave: true, dependent: :destroy
63
+ scope :"with_rich_text_#{name}", -> { includes("rich_text_#{name}") }
64
+ scope :"with_rich_text_#{name}_and_embeds",
65
+ -> { includes("rich_text_#{name}": { embeds_attachments: :blob }) }
66
+ end unless _options[:plain]
67
+ end
68
+
69
+ module ActionTextValidations
70
+ extend ActiveSupport::Concern
71
+
72
+ included do
73
+ validates :name,
74
+ presence: true,
75
+ uniqueness: { scope: %i[record_id record_type locale], case_sensitive: true }
76
+ validates :record, presence: true
77
+ validates :locale, presence: true
78
+ end
79
+ end
80
+
81
+ # Model for translated rich text
82
+ class RichTextTranslation < ::ActionText::RichText
83
+ extend ActionTextValidations
84
+ end
85
+
86
+ # Model for translated plain text
87
+ class PlainTextTranslation < ::ActiveRecord::Base
88
+ self.table_name = "action_text_rich_texts"
89
+
90
+ belongs_to :record, polymorphic: true, touch: true
91
+
92
+ extend ActionTextValidations
93
+ end
94
+ end
95
+
96
+ register_backend(:action_text, ActionText)
97
+ end
98
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/mobility/action_text/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'mobility-actiontext'
7
+ spec.version = Mobility::ActionText::VERSION
8
+ spec.authors = ['Sébastien Dubois']
9
+ spec.email = ['sedubois@users.noreply.github.com']
10
+
11
+ spec.summary = 'Translate Rails Action Text rich text with Mobility.'
12
+ spec.homepage = 'https://github.com/sedubois/mobility-actiontext'
13
+ spec.license = 'MIT'
14
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.2')
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://github.com/sedubois/mobility-actiontext'
18
+ spec.metadata['changelog_uri'] = 'https://github.com/sedubois/mobility-actiontext/blob/main/CHANGELOG.md'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'mobility', '~> 1.1'
30
+ spec.add_dependency 'rails', '~> 6.0'
31
+
32
+ spec.add_development_dependency 'sqlite3', '~> 1.4'
33
+ end