text_helpers 0.6.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ac32959a133d4a65ade46dea9d8759d4d19931eb
4
- data.tar.gz: e3ab723bb5263bfbd2e4747a14cea556ee5d07ea
2
+ SHA256:
3
+ metadata.gz: 97f5f8d490471d60b7cc9286f2fb49460862917a1ed6b6d36c984fb8f5a22ac3
4
+ data.tar.gz: a2fadb5cea94f89c87cf31ef1a7dbb8779739c6aab67d2b9f143968498cc25e7
5
5
  SHA512:
6
- metadata.gz: 703ddbb7c992573ec5ca3cbd01d9960fc981fce5e2fd1fff76f556fd5ab1d07d3f57c99ecc1c3d6a8c33e9c550486fadb908677fbbf10549c3edc4a0830f0619
7
- data.tar.gz: fb75432396bebdb2ba283f572187c5ebb4616563dbad3c6653340034f781c3076c70cbd76628db4533b530c7e56f33e5efd1b7ba609ea450db2b7318460a69ea
6
+ metadata.gz: 73f98e2a518aeafb35653d9d713a3b983d456d2e947bc98fcc1c519d9b8b7034f741eb2985475336e5b9fe6786ebc8502e2fe9994001a259c19c1c0800b769d2
7
+ data.tar.gz: 37a0c2ba7e0510dd38d720abafb43ba6342f1af8b578422b520d82956aa9a46df22b87a6774b615f1665a21b11618f185229face8e8c9b44513c44081cd1f2f0
@@ -1,3 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
3
+ - 2.6.2
4
+ - 2.7.0
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # TextHelpers
2
2
 
3
- `TextHelpers` is a library intended to make working with static text in Rails
4
- projects as painless as possible.
3
+ `TextHelpers` is a library intended to make working with static text in Rails projects as painless as possible.
5
4
 
6
5
  Include it in your `Gemfile` with:
7
6
 
@@ -11,50 +10,23 @@ gem "text_helpers"
11
10
 
12
11
  ## Suggested Use
13
12
 
14
- All static text should be placed in locale files, in a directory
15
- structure mirroring the app directory structure. The text for
16
- `app/views/some/_partial.html.haml` would go in
17
- `config/locales/views/some/partial.en.yml`, for example. This is not a strict
18
- requirement, but will go a long way toward keeping your locales easily
19
- maintainable.
13
+ All static text should be placed in locale files, in a directory structure mirroring the app directory structure. The text for `app/views/some/_partial.html.haml` would go in `config/locales/views/some/partial.en.yml`, for example. This is not a strict requirement, but will go a long way toward keeping your locales easily maintainable.
20
14
 
21
- If you're using this within a Rails project, you'll probably want to add the
22
- following line to your application.rb to ensure that Rails loads any locale
23
- files organized this way:
24
-
25
- ```ruby
26
- config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
27
- ```
28
-
29
- In any locale file entry, you can reference another key in the locale file by
30
- using the syntax `!scope.to.key!`. For the sake of maintainability, I
31
- recommend restricting the use of this feature to small, highly-recycled
32
- fragments of static text. `I18n`'s built-in `%{value}` interpolation can be
33
- used for variable text.
15
+ In any locale file entry, you can reference another key in the locale file by using the syntax `!scope.to.key!`. For the sake of maintainability, I recommend restricting the use of this feature to small, highly-recycled fragments of static text. `I18n`'s built-in `%{value}` interpolation can be used for variable text.
34
16
 
35
17
  ### In Views
36
18
 
37
- To access this text in views, two helpers are available, `text` and `html`.
38
- Both helpers take a lookup key, used to identify the desired piece of text,
39
- and an argument hash, which is forwarded to the `I18n.t` call.
19
+ To access this text in views, two helpers are available, `text` and `html`. Both helpers take a lookup key, used to identify the desired piece of text, and an argument hash, which is forwarded to the `I18n.t` call.
40
20
 
41
- `text` returns the requested text, with special values interpolated, and made
42
- html_safe (so HTML can be used here, when absolutely necessary).
21
+ `text` returns the requested text, with special values interpolated, and made html_safe (so HTML can be used here, when absolutely necessary).
43
22
 
44
- `html` parses the requested text using Markdown, making it useful for rendering
45
- larger pieces of text involving multiple paragraphs, list items or links.
23
+ `html` parses the requested text using Markdown, making it useful for rendering larger pieces of text involving multiple paragraphs, list items or links.
46
24
 
47
- `html` automatically parses Markdown using
48
- [`SmartyPants`-style](http://daringfireball.net/projects/smartypants/)
49
- character conversions, so you can write plain text and have the proper
50
- typographical elements generated for you without having to explicitly insert
51
- HTML entities for common cases.
25
+ `html` automatically parses Markdown using [`SmartyPants`-style](http://daringfireball.net/projects/smartypants/) character conversions, so you can write plain text and have the proper typographical elements generated for you without having to explicitly insert HTML entities for common cases.
52
26
 
53
- If you want to render a small fragment of Markdown without `p` tag wrappers,
54
- you can pass `inline: true` as an option to `html`.
27
+ If you want to render a small fragment of Markdown without `p` tag wrappers, you can pass `inline: true` as an option to `html`.
55
28
 
56
- `text` and `html` will escape all arguments passed to it in order to prevent XSS
57
- attacks. If you want to pass html content, you should ensure you mark it as .html_safe
29
+ `text` and `html` will escape all arguments passed to it in order to prevent XSS attacks. If you want to pass html content, you should ensure you mark it as .html_safe
58
30
 
59
31
  Example: `text('welcome_user', username)` will escape html characters in username
60
32
  ```ruby
@@ -68,14 +40,11 @@ Welcome <b>Bob</b>
68
40
 
69
41
  ### In Controllers
70
42
 
71
- The same helpers are available in controllers, with the translation scope based
72
- on the controller name rather than the view directory. This will typically be
73
- used for flash messages or alerts of some kind.
43
+ The same helpers are available in controllers, with the translation scope based on the controller name rather than the view directory. This will typically be used for flash messages or alerts of some kind.
74
44
 
75
45
  ## Testing
76
46
 
77
- Some shared `RSpec` contexts are available to allow the same locale
78
- abstractions for testing. You can include these contexts with:
47
+ Some shared `RSpec` contexts are available to allow the same locale abstractions for testing. You can include these contexts with:
79
48
 
80
49
  ```
81
50
  require "text_helpers/contexts"
@@ -83,18 +52,17 @@ require "text_helpers/contexts"
83
52
 
84
53
  ### Views
85
54
 
86
- The view text helpers described above can be accessed in view
87
- specs by adding `view: true` to the spec metadata.
55
+ The view text helpers described above can be accessed in view specs by adding `view: true` to the spec metadata.
88
56
 
89
57
  ### Controllers
90
58
 
91
- The controller text helpers described above can be accessed in controller
92
- specs by adding `controller: true` to your spec metadata.
59
+ The controller text helpers described above can be accessed in controller specs by adding `controller: true` to your spec metadata.
93
60
 
94
61
  ### Temporary/Stub Localizations
95
62
 
96
63
  `text_helpers/rspec.rb` contains some helpers for setting up a test localization
97
- environment during your test runs.
64
+ environment during your test runs. You can enable the helper methods by adding
65
+ the `:text_helpers` tag to the examples that require them.
98
66
 
99
67
  To configure it, `require "text_helpers/rspec"` and configure the `before` and
100
68
  `after` hooks appropriately:
@@ -103,11 +71,13 @@ To configure it, `require "text_helpers/rspec"` and configure the `before` and
103
71
  require 'text_helpers/rspec'
104
72
 
105
73
  RSpec.configure do |config|
74
+ config.include TextHelpers::RSpec::TestHelpers, text_helpers: true
75
+
106
76
  config.before(:suite) do
107
77
  TextHelpers::RSpec.setup_spec_translations
108
78
  end
109
79
 
110
- config.after(:each) do
80
+ config.after(:each, :text_helpers) do
111
81
  TextHelpers::RSpec.reset_spec_translations
112
82
  end
113
83
  end
@@ -117,42 +87,43 @@ Temporary localizations can then be defined within your examples via the
117
87
  `#set_translation` method, like so:
118
88
 
119
89
  ```
120
- set_translation('models.user.attributes.name', 'Name')
90
+ describe "with a translation set", :text_helpers do
91
+ before do
92
+ set_translation('models.user.attributes.name', 'Name')
93
+ end
94
+
95
+ it { ... }
96
+ end
121
97
  ```
122
98
 
123
99
  ## Configuration & Initialization
124
100
 
125
101
  ### Initialization
126
102
 
127
- `TextHelpers` performs some setup during your application's initialization. Four
128
- initializers are installed:
103
+ `TextHelpers` performs some setup during your application's initialization. Five initializers are installed:
129
104
 
130
105
  #### `text_helpers.action_view.extend_base`
131
106
 
132
- This initializer includes the `TextHelpers::Translation` module into
133
- `ActionView::Base` and adds an appropriate `#translation_scope` method.
107
+ This initializer includes the `TextHelpers::Translation` module into `ActionView::Base` and adds an appropriate `#translation_scope` method.
134
108
 
135
109
  #### `text_helpers.action_mailer.extend_base`
136
110
 
137
- This initializer includes the `TextHelpers::Translation` module into
138
- `ActionMailer::Base` and adds an appropriate `#translation_scope` method.
111
+ This initializer includes the `TextHelpers::Translation` module into `ActionMailer::Base` and adds an appropriate `#translation_scope` method.
139
112
 
140
113
  #### `text_helpers.action_controller.extend_base`
141
114
 
142
- This initializer includes the `TextHelpers::Translation` module into
143
- `ActionController::Base` and adds an appropriate `#translation_scope` method.
115
+ This initializer includes the `TextHelpers::Translation` module into `ActionController::Base` and adds an appropriate `#translation_scope` method.
116
+
117
+ #### `text_helpers.i18n.add_load_paths`
118
+
119
+ This initializer updates the default I18n locale file load paths for your Rails application to recursively include files within directories and subdirectories. This enables a more hierarchical organization of your locale files.
144
120
 
145
121
  #### `text_helpers.setup_exception_handling`
146
122
 
147
- This initializer configures exception handling so that exceptions are raised
148
- if `config.text_helpers.raise_on_missing_translations` is set to `true`, which
149
- it is by default in the `test` or `development` environments.
123
+ This initializer configures exception handling so that exceptions are raised if `config.text_helpers.raise_on_missing_translations` is set to `true`, which it is by default in the `test` or `development` environments.
150
124
 
151
125
  ### Configuration
152
126
 
153
127
  #### `config.text_helpers.raise_on_missing_translations`
154
128
 
155
- This configuration value defaults to `true` in `test` or `development`
156
- environments. If set to `false`, your own exception handling can be configured
157
- by setting `config.action_view.raise_on_missing_translations` and
158
- `I18n.exception_handler` as appropriate.
129
+ This configuration value defaults to `true` in `test` or `development` environments. If set to `false`, your own exception handling can be configured by setting `config.action_view.raise_on_missing_translations` and `I18n.exception_handler` as appropriate.
data/Rakefile CHANGED
@@ -4,6 +4,7 @@ require 'rake/testtask'
4
4
 
5
5
  Rake::TestTask.new do |t|
6
6
  t.pattern = "test/**/*_test.rb"
7
+ t.warning = false
7
8
  end
8
9
 
9
10
  task default: :test
@@ -5,10 +5,10 @@ require "text_helpers/railtie" if defined?(Rails)
5
5
  module TextHelpers
6
6
  # RaiseExceptionHandler just raises all exceptions, rather than swallowing
7
7
  # MissingTranslation ones. It's cribbed almost verbatim from
8
- # http://edgeguides.rubyonrails.org/i18n.html#customize-your-i18n-setup.
8
+ # https://guides.rubyonrails.org/i18n.html#using-different-exception-handlers.
9
9
  class RaiseExceptionHandler < I18n::ExceptionHandler
10
10
  def call(exception, locale, key, options)
11
- if exception.is_a?(I18n::MissingTranslation)
11
+ if exception.is_a?(I18n::MissingTranslation) && key.to_s != "i18n.plural.rule"
12
12
  raise exception.to_exception
13
13
  else
14
14
  super
@@ -1,4 +1,4 @@
1
- shared_context "a controller spec", controller: true do
1
+ RSpec.shared_context "a controller spec", controller: true do
2
2
  include TextHelpers::Translation
3
3
 
4
4
  def translation_scope
@@ -7,7 +7,7 @@ shared_context "a controller spec", controller: true do
7
7
  end
8
8
  end
9
9
 
10
- shared_context "a mailer spec", mailer: true do
10
+ RSpec.shared_context "a mailer spec", mailer: true do
11
11
  include TextHelpers::Translation
12
12
 
13
13
  def translation_scope
@@ -16,7 +16,7 @@ shared_context "a mailer spec", mailer: true do
16
16
  end
17
17
  end
18
18
 
19
- shared_context "a view spec", view: true do
19
+ RSpec.shared_context "a view spec", view: true do
20
20
  include TextHelpers::Translation
21
21
 
22
22
  def translation_scope
@@ -57,6 +57,11 @@ module TextHelpers
57
57
  end
58
58
  end
59
59
 
60
+ initializer "text_helpers.i18n.add_load_paths" do |app|
61
+ locales = Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s
62
+ app.config.i18n.load_path += Dir[locales]
63
+ end
64
+
60
65
  initializer "text_helpers.setup_exception_handling", after: 'after_initialize' do
61
66
  next unless config.text_helpers.raise_on_missing_translations
62
67
 
@@ -22,7 +22,8 @@ module TextHelpers
22
22
 
23
23
  module Translation
24
24
 
25
- ORPHAN_MATCHER = /\s(?![^<]*>)(\S+\s*<\/(?:p|li)>)/.freeze
25
+ ORPHAN_MATCHER = /(\w+)[ \t](?![^<]*>)(\S+\s*<\/(?:p|li)>)/.freeze
26
+ KEYPATH_MATCHER = /!([\w.\/]+)!/.freeze
26
27
 
27
28
  # Public: Get the I18n localized text for the passed key.
28
29
  #
@@ -34,17 +35,17 @@ module TextHelpers
34
35
  # Returns a String resulting from the I18n lookup.
35
36
  def text(key, options = {})
36
37
  options = html_safe_options(options)
37
- text = I18n.t(key, {
38
+ text = I18n.t(key, **{
38
39
  scope: self.translation_scope,
39
- default: "!#{key}!"
40
+ default: "!#{key}!",
41
+ cascade: true,
40
42
  }.merge(options)).strip
41
43
 
42
- interpolation_options = options.dup
43
- interpolation_options[:cascade] = true unless interpolation_options.has_key?(:cascade)
44
+ interpolation_options = { cascade: true }.merge(options)
44
45
 
45
46
  # Interpolate any keypaths (e.g., `!some.lookup.path/key!`) found in the text.
46
- while text =~ /!([\w._\/]+)!/ do
47
- text = text.gsub(/!([\w._\/]+)!/) { |match| I18n.t($1, interpolation_options) }
47
+ while text =~ KEYPATH_MATCHER do
48
+ text = text.gsub(KEYPATH_MATCHER) { |match| I18n.t($1, **interpolation_options) }
48
49
  end
49
50
 
50
51
  text = smartify(text) if options.fetch(:smart, true)
@@ -65,7 +66,7 @@ module TextHelpers
65
66
  def html(key, options = {})
66
67
  rendered = markdown(text(key, options.merge(smart: false)))
67
68
 
68
- rendered = options[:orphans] ? rendered : rendered.gsub(ORPHAN_MATCHER, '&nbsp;\1')
69
+ rendered = options[:orphans] ? rendered : rendered.gsub(ORPHAN_MATCHER, '\1&nbsp;\2')
69
70
  rendered = rendered.gsub(/<\/?p>/, '') if options[:inline]
70
71
  rendered.html_safe
71
72
  end
@@ -82,7 +83,7 @@ module TextHelpers
82
83
  smartify(@renderer.render(text))
83
84
  end
84
85
 
85
- # Protected: Auto-apply smart quotes and to the passed text.
86
+ # Internal: Auto-apply smart quotes to the passed text.
86
87
  #
87
88
  # text - A String which should be passed through the SmartyPants renderer.
88
89
  #
@@ -91,16 +92,16 @@ module TextHelpers
91
92
  Redcarpet::Render::SmartyPants.render(text)
92
93
  end
93
94
 
94
- # Protected: The proper scope for I18n translation.
95
+ # Internal: The proper scope for I18n translation.
95
96
  #
96
97
  # Must be implemented by any classes which include this module.
97
98
  #
98
99
  # Raises NotImplementedError.
99
100
  def translation_scope
100
- raise NotImplementedError
101
+ raise NotImplementedError, "must implement a public method `translation_scope` to determine I18n scope"
101
102
  end
102
103
 
103
- # Protected: Convert all passed in arguments into html-safe strings
104
+ # Internal: Convert all passed in arguments into html-safe strings
104
105
  #
105
106
  # hash - a set of key-value pairs, which converts the second argument into an html-safe string
106
107
  #
@@ -1,3 +1,3 @@
1
1
  module TextHelpers
2
- VERSION = "0.6.1"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -1,4 +1,4 @@
1
- require_relative '../../test_helper'
1
+ require_relative "../../test_helper"
2
2
 
3
3
  describe TextHelpers::Translation do
4
4
  before do
@@ -9,6 +9,7 @@ describe TextHelpers::Translation do
9
9
  before do
10
10
  @scoped_text = "Scoped lookup"
11
11
  @global_text = "Global lookup"
12
+ @single_word_text = "Single"
12
13
  @email_address = "user@example.org"
13
14
  @multiline_text = <<-MULTI.gsub(/^[ \t]+/, '')
14
15
  This is some multiline text.
@@ -30,6 +31,8 @@ describe TextHelpers::Translation do
30
31
  email_key: "<#{@email_address}>",
31
32
  test_key: "*#{@scoped_text}*",
32
33
  list_key: "* #{@scoped_text}",
34
+ single_word_list_key: "* #{@single_word_text}",
35
+ prerendered_html_key: "<ul>\n <li> Get everything you ever wanted</li>\n <li> Practically-guaranteed</li>\n </ul>",
33
36
  interpolated_key: "Global? (!test_key!)",
34
37
  interpolated_scoped_key: "Global? (!test_scoped_key!)",
35
38
  interpol_arg_key: "Interpolate global? (!interpolated_key!)",
@@ -74,6 +77,22 @@ describe TextHelpers::Translation do
74
77
  assert_equal expected, @helper.html(:list_key)
75
78
  end
76
79
 
80
+ it "does not inject `&nbsp;` entities in HTML list items unnecessarily" do
81
+ expected = <<-EXPECTED.gsub(/^[ \t]+/, '')
82
+ <ul>
83
+ <li>#{@single_word_text}</li>
84
+ </ul>
85
+ EXPECTED
86
+
87
+ assert_equal expected, @helper.html(:single_word_list_key)
88
+ end
89
+
90
+ it "correctly handles orphans in HTML with erratic whitespace" do
91
+ expected = "<ul>\n <li> Get everything you ever&nbsp;wanted</li>\n <li> Practically-guaranteed</li>\n </ul>\n"
92
+
93
+ assert_equal expected, @helper.html(:prerendered_html_key)
94
+ end
95
+
77
96
  it "does not modify HTML tags" do
78
97
  expected = "<p><a href=\"mailto:#{@email_address}\">#{@email_address}</a></p>\n"
79
98
  assert_equal expected, @helper.html(:email_key)
@@ -152,10 +171,32 @@ describe TextHelpers::Translation do
152
171
  assert_equal "This is what <b>Han</b> Solo said", @helper.text(:argument_key, user: "<b>Han</b> Solo".html_safe)
153
172
  end
154
173
 
155
- it "correctly handles non-string params" do
174
+ it "correctly handles pluralized keys" do
156
175
  assert_equal "A single piece of text", @helper.text(:pluralized_key, count: 1)
157
176
  assert_equal "2 pieces of text", @helper.text(:pluralized_key, count: 2)
158
177
  end
178
+
179
+ describe "when the pluralization backend is configured and the exception handler is enabled" do
180
+ before do
181
+ @original_backend = I18n.backend
182
+ new_backend = @original_backend.dup
183
+ new_backend.extend(I18n::Backend::Pluralization)
184
+ I18n.backend = new_backend
185
+
186
+ @original_exception_handler = I18n.exception_handler
187
+ I18n.exception_handler = TextHelpers::RaiseExceptionHandler.new
188
+ end
189
+
190
+ after do
191
+ I18n.backend = @original_backend
192
+ I18n.exception_handler = @original_exception_handler
193
+ end
194
+
195
+ it "correctly handles pluralized keys" do
196
+ assert_equal "A single piece of text", @helper.text(:pluralized_key, count: 1)
197
+ assert_equal "2 pieces of text", @helper.text(:pluralized_key, count: 2)
198
+ end
199
+ end
159
200
  end
160
201
 
161
202
  describe "when no valid scope is provided" do
@@ -201,6 +242,14 @@ describe TextHelpers::Translation do
201
242
  I18n.backend = @original_backend
202
243
  end
203
244
 
245
+ it "cascades the requested key by default" do
246
+ I18n.backend.store_translations(:en, {test_scoped_key: "a translation"})
247
+ assert_equal "a translation", @helper.text(:test_scoped_key, scope: "some.unnecessary.scope")
248
+
249
+ I18n.backend.store_translations(:en, {some: {test_scoped_key: "a scoped translation"}})
250
+ assert_equal "a scoped translation", @helper.text(:test_scoped_key, scope: "some.unnecessary.scope")
251
+ end
252
+
204
253
  it "cascades the interpolated key by default" do
205
254
  I18n.backend.store_translations(:en, {test_scoped_key: "a translation"})
206
255
 
@@ -3,6 +3,6 @@ require_relative "../../test_helper"
3
3
  describe TextHelpers do
4
4
 
5
5
  it "defines a version" do
6
- TextHelpers::VERSION.wont_be_nil
6
+ assert TextHelpers::VERSION
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: text_helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-01 00:00:00.000000000 Z
11
+ date: 2020-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,7 +108,7 @@ homepage: https://github.com/ahorner/text-helpers
108
108
  licenses:
109
109
  - MIT
110
110
  metadata: {}
111
- post_install_message:
111
+ post_install_message:
112
112
  rdoc_options: []
113
113
  require_paths:
114
114
  - lib
@@ -123,9 +123,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubyforge_project:
127
- rubygems_version: 2.4.5
128
- signing_key:
126
+ rubygems_version: 3.1.4
127
+ signing_key:
129
128
  specification_version: 4
130
129
  summary: TextHelpers is a gem which supplies some basic utilities for text localization
131
130
  in Rails projects. The library is intended to make it simple to keep your application's