inline_svg 1.0.0 → 1.10.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 (63) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/integration_test.yml +47 -0
  3. data/.github/workflows/rails_6_webpacker_integration_tests.yaml +62 -0
  4. data/.github/workflows/ruby.yml +20 -0
  5. data/.rubocop.yml +1 -0
  6. data/.rubocop_todo.yml +421 -0
  7. data/CHANGELOG.md +143 -3
  8. data/README.md +149 -30
  9. data/Rakefile +7 -0
  10. data/inline_svg.gemspec +2 -1
  11. data/lib/inline_svg/action_view/helpers.rb +68 -7
  12. data/lib/inline_svg/cached_asset_file.rb +71 -0
  13. data/lib/inline_svg/finds_asset_paths.rb +1 -1
  14. data/lib/inline_svg/id_generator.rb +12 -3
  15. data/lib/inline_svg/io_resource.rb +4 -3
  16. data/lib/inline_svg/propshaft_asset_finder.rb +16 -0
  17. data/lib/inline_svg/railtie.rb +8 -3
  18. data/lib/inline_svg/static_asset_finder.rb +5 -2
  19. data/lib/inline_svg/transform_pipeline/transformations/aria_attributes.rb +16 -19
  20. data/lib/inline_svg/transform_pipeline/transformations/aria_hidden.rb +9 -0
  21. data/lib/inline_svg/transform_pipeline/transformations/aria_hidden_attribute.rb +9 -0
  22. data/lib/inline_svg/transform_pipeline/transformations/class_attribute.rb +5 -6
  23. data/lib/inline_svg/transform_pipeline/transformations/data_attributes.rb +4 -5
  24. data/lib/inline_svg/transform_pipeline/transformations/description.rb +7 -6
  25. data/lib/inline_svg/transform_pipeline/transformations/height.rb +3 -4
  26. data/lib/inline_svg/transform_pipeline/transformations/id_attribute.rb +3 -4
  27. data/lib/inline_svg/transform_pipeline/transformations/no_comment.rb +4 -4
  28. data/lib/inline_svg/transform_pipeline/transformations/preserve_aspect_ratio.rb +3 -4
  29. data/lib/inline_svg/transform_pipeline/transformations/size.rb +4 -5
  30. data/lib/inline_svg/transform_pipeline/transformations/style_attribute.rb +11 -0
  31. data/lib/inline_svg/transform_pipeline/transformations/title.rb +7 -6
  32. data/lib/inline_svg/transform_pipeline/transformations/transformation.rb +13 -0
  33. data/lib/inline_svg/transform_pipeline/transformations/view_box.rb +9 -0
  34. data/lib/inline_svg/transform_pipeline/transformations/width.rb +3 -4
  35. data/lib/inline_svg/transform_pipeline/transformations.rb +11 -2
  36. data/lib/inline_svg/transform_pipeline.rb +1 -1
  37. data/lib/inline_svg/version.rb +1 -1
  38. data/lib/inline_svg/webpack_asset_finder.rb +60 -0
  39. data/lib/inline_svg.rb +46 -9
  40. data/spec/cached_asset_file_spec.rb +73 -0
  41. data/spec/files/static_assets/assets0/known-document-two.svg +1 -0
  42. data/spec/files/static_assets/assets0/known-document.svg +1 -0
  43. data/spec/files/static_assets/assets0/some-document.svg +1 -0
  44. data/spec/files/static_assets/assets1/known-document.svg +1 -0
  45. data/spec/files/static_assets/assets1/other-document.svg +3 -0
  46. data/spec/files/static_assets/assets1/some-file.txt +1 -0
  47. data/spec/finds_asset_paths_spec.rb +45 -0
  48. data/spec/helpers/inline_svg_spec.rb +117 -51
  49. data/spec/id_generator_spec.rb +5 -3
  50. data/spec/inline_svg_spec.rb +48 -0
  51. data/spec/propshaft_asset_finder_spec.rb +23 -0
  52. data/spec/static_asset_finder_spec.rb +25 -0
  53. data/spec/transformation_pipeline/transformations/aria_attributes_spec.rb +6 -6
  54. data/spec/transformation_pipeline/transformations/aria_hidden_attribute_spec.rb +12 -0
  55. data/spec/transformation_pipeline/transformations/height_spec.rb +9 -0
  56. data/spec/transformation_pipeline/transformations/style_attribute_spec.rb +26 -0
  57. data/spec/transformation_pipeline/transformations/title_spec.rb +9 -0
  58. data/spec/transformation_pipeline/transformations/transformation_spec.rb +39 -0
  59. data/spec/transformation_pipeline/transformations/view_box_spec.rb +13 -0
  60. data/spec/transformation_pipeline/transformations_spec.rb +7 -1
  61. data/spec/webpack_asset_finder_spec.rb +23 -0
  62. metadata +62 -10
  63. data/circle.yml +0 -3
data/CHANGELOG.md CHANGED
@@ -3,7 +3,129 @@ All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased][unreleased]
6
- - Nothing
6
+
7
+ Nothing.
8
+
9
+ ## [1.10.0] - 2024-09-03
10
+ ### Added
11
+ - Support for Shakapacker. [#158](https://github.com/jamesmartin/inline_svg/pull/158). Thanks, [@tagliala](https://github.com/tagliala)
12
+
13
+ ### Fixed
14
+ - Fixed documentation typos. [#157](https://github.com/jamesmartin/inline_svg/pull/157). Thanks, [@tagliala](https://github.com/tagliala)
15
+ - Fixed missing ActiveSupport require. [#152](https://github.com/jamesmartin/inline_svg/pull/152). Thanks, [@xymbol](https://github.com/xymbol)
16
+ - Remove wrapping whitespace from SVG tags. [#150](https://github.com/jamesmartin/inline_svg/pull/150). Thanks, [@fredboyle](https://github.com/fredboyle)
17
+
18
+ ## [1.9.0] - 2023-03-29
19
+ ### Added
20
+ - A new option: `view_box` adds a `viewBox` attribute to the SVG. [#142](https://github.com/jamesmartin/inline_svg/pull/142). Thanks [@sunny](https://github.com/sunny)
21
+
22
+ ### Fixed
23
+ - Allow Propshaft assets to use fallbacks. [#140](https://github.com/jamesmartin/inline_svg/pull/140). Thanks, [@ohrite](https://github.com/ohrite)
24
+ - Handling missing file when using static assets. [#141](https://github.com/jamesmartin/inline_svg/pull/141). Thanks, [@leighhalliday](https://github.com/leighhalliday)
25
+ - Handle missing file when using Webpacker assets.
26
+
27
+ ## [1.8.0] - 2022-01-09
28
+ ### Added
29
+ - Remove deprecation warning for `inline_svg`, as we intend to keep it in 2.0. [#131](https://github.com/jamesmartin/inline_svg/pull/131). Thanks [@DanielJackson-Oslo](https://github.com/DanielJackson-Oslo)
30
+ - Add support for Webpacker 6 beta. [#129](https://github.com/jamesmartin/inline_svg/pull/129). Thanks [@Intrepidd](https://github.com/Intrepidd) and [@tessi](https://github.com/tessi)
31
+ - Add support for Propshaft assets in Rails 7. [#134](https://github.com/jamesmartin/inline_svg/pull/134). Thanks, [@martinzamuner](https://github.com/martinzamuner)
32
+
33
+ ## [1.7.2] - 2020-12-07
34
+ ### Fixed
35
+ - Improve performance of `CachedAssetFile`. [#118](https://github.com/jamesmartin/inline_svg/pull/118). Thanks [@stevendaniels](https://github.com/stevendaniels)
36
+ - Avoid XSS by preventing malicious input of filenames. [#117](https://github.com/jamesmartin/inline_svg/pull/117). Thanks [@pbyrne](https://github.com/pbyrne).
37
+
38
+ ## [1.7.1] - 2020-03-17
39
+ ### Fixed
40
+ - Static Asset Finder uses pathname for compatibility with Sprockets 4+. [#106](https://github.com/jamesmartin/inline_svg/pull/106). Thanks [@subdigital](https://github.com/subdigital)
41
+
42
+ ## [1.7.0] - 2020-02-13
43
+ ### Added
44
+ - WebpackAssetFinder serves files from dev server if one is running. [#111](https://github.com/jamesmartin/inline_svg/pull/111). Thanks, [@connorshea](https://github.com/connorshea)
45
+
46
+ ### Fixed
47
+ - Using Webpacker and Asset Pipeline in a single App could result in SVGs not being found because the wrong `AssetFinder` was used. [#114](https://github.com/jamesmartin/inline_svg/pull/114). Thanks, [@kylefox](https://github.com/kylefox)
48
+ - Prevent "EOFError error" when using webpack dev server over HTTPS [#113](https://github.com/jamesmartin/inline_svg/pull/113). Thanks, [@kylefox](https://github.com/kylefox)
49
+
50
+ ## [1.6.0] - 2019-11-13
51
+ ### Added
52
+ - Support Webpack via the new `inline_svg_pack_tag` helper and deprecate `inline_svg` helper in preparation for v2.0.
53
+ [#103](https://github.com/jamesmartin/inline_svg/pull/103)
54
+ Thanks, [@kylefox](https://github.com/kylefox)
55
+
56
+ ## [1.5.2] - 2019-06-20
57
+ ### Fixed
58
+ - Revert automatic Webpack asset finder behavior. Make Webpack "opt-in".
59
+ [#98](https://github.com/jamesmartin/inline_svg/issues/98)
60
+
61
+ ## [1.5.1] - 2019-06-18
62
+ ### Fixed
63
+ - Prevent nil asset finder when neither Sprockets or Webpacker are available
64
+ [#97](https://github.com/jamesmartin/inline_svg/issues/97)
65
+
66
+ ## [1.5.0] - 2019-06-17
67
+ ### Added
68
+ - Support for finding assets bundled by Webpacker
69
+ [#96](https://github.com/jamesmartin/inline_svg/pull/96)
70
+
71
+ ## [1.4.0] - 2019-04-19
72
+ ### Fixed
73
+ - Prevent invalid XML names being generated via IdGenerator
74
+ [#87](https://github.com/jamesmartin/inline_svg/issues/87)
75
+ Thanks, [@endorfin](https://github.com/endorfin)
76
+
77
+ ### Added
78
+ - Raise error on file not found (if configured)
79
+ [#93](https://github.com/jamesmartin/inline_svg/issues/93)
80
+
81
+ ## [1.3.1] - 2017-12-14
82
+ ### Fixed
83
+ - Allow Ruby < 2.1 to work with `CachedAssetFile`
84
+ [#80](https://github.com/jamesmartin/inline_svg/pull/80)
85
+
86
+ ## [1.3.0] - 2017-10-30
87
+ ### Added
88
+ - Aria hidden attribute
89
+ [#78](https://github.com/jamesmartin/inline_svg/pull/78)
90
+ and [#79](https://github.com/jamesmartin/inline_svg/pull/79)
91
+ - In-line CSS style attribute
92
+ [#71](https://github.com/jamesmartin/inline_svg/pull/71)
93
+
94
+ ### Fixed
95
+ - Make aria ID attributes unique
96
+ [#77](https://github.com/jamesmartin/inline_svg/pull/77)
97
+
98
+ ## [1.2.3] - 2017-08-17
99
+ ### Fixed
100
+ - Handle UTF-8 characters in SVG documents
101
+ [#60](https://github.com/jamesmartin/inline_svg/pull/69)
102
+
103
+ ## [1.2.2] - 2017-07-06
104
+ ### Fixed
105
+ - Handle malformed documents that don't contain a root SVG element
106
+ [#60](https://github.com/jamesmartin/inline_svg/pull/65)
107
+ ### Added
108
+ - Add configurable CSS class to empty SVG document
109
+ [#67](https://github.com/jamesmartin/inline_svg/pull/67)
110
+
111
+ ## [1.2.1] - 2017-05-02
112
+ ### Fixed
113
+ - Select most exactly matching cached asset file when multiple files match
114
+ given asset name [#64](https://github.com/jamesmartin/inline_svg/pull/64)
115
+
116
+ ## [1.2.0] - 2017-04-20
117
+ ### Added
118
+ - Cached asset file (load assets into memory at boot time)
119
+ [#62](https://github.com/jamesmartin/inline_svg/pull/62)
120
+
121
+ ## [1.1.0] - 2017-04-12
122
+ ### Added
123
+ - Allow configurable asset file implementations
124
+ [#61](https://github.com/jamesmartin/inline_svg/pull/61)
125
+
126
+ ## [1.0.1] - 2017-04-10
127
+ ### Fixed
128
+ - Don't override custom asset finders in Railtie
7
129
 
8
130
  ## [1.0.0] - 2017-04-7
9
131
  ### Added
@@ -99,7 +221,7 @@ transformations](https://github.com/jamesmartin/inline_svg/blob/master/README.md
99
221
 
100
222
  ## [0.5.1] - 2015-03-30
101
223
  ### Warning
102
- ** This version is NOT comaptible with Sprockets >= 3. **
224
+ ** This version is NOT compatible with Sprockets >= 3. **
103
225
 
104
226
  ### Fixed
105
227
  - Support for ActiveSupport (and hence, Rails) 4.2.x. Thanks, @jmarceli.
@@ -140,7 +262,25 @@ transformations](https://github.com/jamesmartin/inline_svg/blob/master/README.md
140
262
  ### Added
141
263
  - Basic Railtie and view helper to inline SVG documents to Rails views.
142
264
 
143
- [unreleased]: https://github.com/jamesmartin/inline_svg/compare/v1.0.0...HEAD
265
+ [unreleased]: https://github.com/jamesmartin/inline_svg/compare/v1.9.0...HEAD
266
+ [1.9.0]: https://github.com/jamesmartin/inline_svg/compare/v1.8.0...v1.9.0
267
+ [1.8.0]: https://github.com/jamesmartin/inline_svg/compare/v1.7.2...v1.8.0
268
+ [1.7.2]: https://github.com/jamesmartin/inline_svg/compare/v1.7.1...v1.7.2
269
+ [1.7.1]: https://github.com/jamesmartin/inline_svg/compare/v1.7.0...v1.7.1
270
+ [1.7.0]: https://github.com/jamesmartin/inline_svg/compare/v1.6.0...v1.7.0
271
+ [1.6.0]: https://github.com/jamesmartin/inline_svg/compare/v1.5.2...v1.6.0
272
+ [1.5.2]: https://github.com/jamesmartin/inline_svg/compare/v1.5.1...v1.5.2
273
+ [1.5.1]: https://github.com/jamesmartin/inline_svg/compare/v1.5.0...v1.5.1
274
+ [1.5.0]: https://github.com/jamesmartin/inline_svg/compare/v1.4.0...v1.5.0
275
+ [1.4.0]: https://github.com/jamesmartin/inline_svg/compare/v1.3.1...v1.4.0
276
+ [1.3.1]: https://github.com/jamesmartin/inline_svg/compare/v1.3.0...v1.3.1
277
+ [1.3.0]: https://github.com/jamesmartin/inline_svg/compare/v1.2.3...v1.3.0
278
+ [1.2.3]: https://github.com/jamesmartin/inline_svg/compare/v1.2.2...v1.2.3
279
+ [1.2.2]: https://github.com/jamesmartin/inline_svg/compare/v1.2.1...v1.2.2
280
+ [1.2.1]: https://github.com/jamesmartin/inline_svg/compare/v1.2.0...v1.2.1
281
+ [1.2.0]: https://github.com/jamesmartin/inline_svg/compare/v1.1.0...v1.2.0
282
+ [1.1.0]: https://github.com/jamesmartin/inline_svg/compare/v1.0.1...v1.1.0
283
+ [1.0.1]: https://github.com/jamesmartin/inline_svg/compare/v1.0.0...v1.0.1
144
284
  [1.0.0]: https://github.com/jamesmartin/inline_svg/compare/v0.12.1...v1.0.0
145
285
  [0.12.1]: https://github.com/jamesmartin/inline_svg/compare/v0.12.0...v0.12.1
146
286
  [0.12.0]: https://github.com/jamesmartin/inline_svg/compare/v0.11.1...v0.12.0
data/README.md CHANGED
@@ -1,13 +1,22 @@
1
1
  # Inline SVG
2
2
 
3
+ ![Unit tests](https://github.com/jamesmartin/inline_svg/workflows/Ruby/badge.svg)
4
+ ![Integration Tests](https://github.com/jamesmartin/inline_svg/workflows/Integration%20Tests/badge.svg)
5
+
3
6
  Styling a SVG document with CSS for use on the web is most reliably achieved by
4
7
  [adding classes to the document and
5
8
  embedding](http://css-tricks.com/using-svg/) it inline in the HTML.
6
9
 
7
- This gem adds a Rails helper method (`inline_svg`) that reads an SVG document (via Sprockets, so works with the Rails Asset Pipeline), applies a CSS class attribute to the root of the document and
10
+ This gem adds Rails helper methods (`inline_svg_tag` and `inline_svg_pack_tag`) that read an SVG document (via Sprockets or Webpacker, so works with the Rails Asset Pipeline), applies a CSS class attribute to the root of the document and
8
11
  then embeds it into a view.
9
12
 
10
- Inline SVG supports [Rails 3](http://weblog.rubyonrails.org/2010/8/29/rails-3-0-it-s-done/) (from [v0.12.0](https://github.com/jamesmartin/inline_svg/releases/tag/v0.12.0)), [Rails 4](http://weblog.rubyonrails.org/2013/6/25/Rails-4-0-final/) and [Rails 5](http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/) (from [v0.10.0](https://github.com/jamesmartin/inline_svg/releases/tag/v0.10.0)).
13
+ Inline SVG supports:
14
+
15
+ - [Rails 5](http://weblog.rubyonrails.org/2016/6/30/Rails-5-0-final/) (from [v0.10.0](https://github.com/jamesmartin/inline_svg/releases/tag/v0.10.0))
16
+ - [Rails 6](https://weblog.rubyonrails.org/2019/4/24/Rails-6-0-rc1-released/) with Sprockets or Webpacker (from [v1.5.2](https://github.com/jamesmartin/inline_svg/releases/tag/v1.5.2)).
17
+ - [Rails 7](https://weblog.rubyonrails.org/2021/12/6/Rails-7-0-rc-1-released/)
18
+
19
+ Inline SVG no longer officially supports Rails 3 or Rails 4 (although they may still work). In order to reduce the maintenance cost of this project we now follow the [Rails Maintenance Policy](https://guides.rubyonrails.org/maintenance_policy.html).
11
20
 
12
21
  ## Changelog
13
22
 
@@ -30,9 +39,15 @@ Or install it yourself as:
30
39
 
31
40
  ## Usage
32
41
 
42
+ ```ruby
43
+ # Sprockets
44
+ inline_svg_tag(file_name, options={})
45
+
46
+ # Webpacker
47
+ inline_svg_pack_tag(file_name, options={})
33
48
  ```
34
- inline_svg(file_name, options={})
35
- ```
49
+
50
+ _**Note:** The remainder of this README uses `inline_svg_tag` for examples, but the exact same principles work for `inline_svg_pack_tag`._
36
51
 
37
52
  The `file_name` can be a full path to a file, the file's basename or an `IO`
38
53
  object. The
@@ -40,18 +55,20 @@ actual path of the file on disk is resolved using
40
55
  [Sprockets](://github.com/sstephenson/sprockets) (when available), a naive file finder (`/public/assets/...`) or in the case of `IO` objects the SVG data is read from the object.
41
56
  This means you can pre-process and fingerprint your SVG files like other Rails assets, or choose to find SVG data yourself.
42
57
 
43
- Here's an example of embedding an SVG document and applying a 'class' attribute in
44
- HAML:
58
+ Here's an example of embedding an SVG document and applying a 'class' attribute:
45
59
 
46
- ```haml
47
- !!! 5
48
- %html
49
- %head
50
- %title Embedded SVG Documents
51
- %body
52
- %h1 Embedded SVG Documents
53
- %div
54
- = inline_svg "some-document.svg", class: 'some-class'
60
+ ```erb
61
+ <html>
62
+ <head>
63
+ <title>Embedded SVG Documents<title>
64
+ </head>
65
+ <body>
66
+ <h1>Embedded SVG Documents</h1>
67
+ <div>
68
+ <%= inline_svg_tag "some-document.svg", class: 'some-class' %>
69
+ </div>
70
+ </body>
71
+ </html>
55
72
  ```
56
73
 
57
74
  Here's some CSS to target the SVG, resize it and turn it an attractive shade of
@@ -67,30 +84,47 @@ blue:
67
84
  }
68
85
  ```
69
86
 
70
- ## Options
87
+ ## Options
71
88
 
72
89
  key | description
73
- :---------------------- | :----------
90
+ :---------------------- | :----------
74
91
  `id` | set a ID attribute on the SVG
75
92
  `class` | set a CSS class attribute on the SVG
93
+ `style` | set a CSS style attribute on the SVG
76
94
  `data` | add data attributes to the SVG (supply as a hash)
77
95
  `size` | set width and height attributes on the SVG <br/> Can also be set using `height` and/or `width` attributes, which take precedence over `size` <br/> Supplied as "{Width} * {Height}" or "{Number}", so "30px\*45px" becomes `width="30px"` and `height="45px"`, and "50%" becomes `width="50%"` and `height="50%"`
78
96
  `title` | add a \<title\> node inside the top level of the SVG document
79
97
  `desc` | add a \<desc\> node inside the top level of the SVG document
80
98
  `nocomment` | remove comment tags from the SVG document
81
99
  `preserve_aspect_ratio` | adds a `preserveAspectRatio` attribute to the SVG
100
+ `view_box` | adds a `viewBox` attribute to the SVG
82
101
  `aria` | adds common accessibility attributes to the SVG (see [PR #34](https://github.com/jamesmartin/inline_svg/pull/34#issue-152062674) for details)
102
+ `aria_hidden` | adds the `aria-hidden=true` attribute to the SVG
103
+ `fallback` | set fallback SVG document
83
104
 
84
105
  Example:
85
106
 
86
107
  ```ruby
87
- inline_svg("some-document.svg", id: 'some-id', class: 'some-class', data: {some: "value"}, size: '30% * 20%', title: 'Some Title', desc:
88
- 'Some description', nocomment: true, preserve_aspect_ratio: 'xMaxYMax meet', aria: true)
108
+ inline_svg_tag(
109
+ "some-document.svg",
110
+ id: 'some-id',
111
+ class: 'some-class',
112
+ data: {some: "value"},
113
+ size: '30% * 20%',
114
+ title: 'Some Title',
115
+ desc: 'Some description',
116
+ nocomment: true,
117
+ preserve_aspect_ratio: 'xMaxYMax meet',
118
+ view_box: '0 0 100 100',
119
+ aria: true,
120
+ aria_hidden: true,
121
+ fallback: 'fallback-document.svg'
122
+ )
89
123
  ```
90
124
 
91
125
  ## Accessibility
92
126
 
93
- Use the `aria: true` option to make `inline_svg` add the following
127
+ Use the `aria: true` option to make `inline_svg_tag` add the following
94
128
  accessibility (a11y) attributes to your embedded SVG:
95
129
 
96
130
  * Adds a `role="img"` attribute to the root SVG element
@@ -101,7 +135,7 @@ Here's an example:
101
135
 
102
136
  ```erb
103
137
  <%=
104
- inline_svg('iconmonstr-glasses-12-icon.svg',
138
+ inline_svg_tag('iconmonstr-glasses-12-icon.svg',
105
139
  aria: true, title: 'An SVG',
106
140
  desc: 'This is my SVG. There are many like it. You get the picture')
107
141
  %>
@@ -115,11 +149,11 @@ Here's an example:
115
149
  </svg>
116
150
  ```
117
151
 
118
- ***Note:*** The title and desc `id` attributes generated for, and referenced by, `aria-labelled-by` are one-way digests based on the value of the title and desc elements and an optional "salt" value using the SHA1 algorithm. This reduces the chance of `inline_svg` embedding elements inside the SVG with `id` attributes that clash with other elements elsewhere on the page.
152
+ ***Note:*** The title and desc `id` attributes generated for, and referenced by, `aria-labelled-by` are one-way digests based on the value of the title and desc elements and an optional "salt" value using the SHA1 algorithm. This reduces the chance of `inline_svg_tag` embedding elements inside the SVG with `id` attributes that clash with other elements elsewhere on the page.
119
153
 
120
154
  ## Custom Transformations
121
155
 
122
- The transformation behavior of `inline_svg` can be customized by creating custom transformation classes.
156
+ The transformation behavior of `inline_svg_tag` can be customized by creating custom transformation classes.
123
157
 
124
158
  For example, inherit from `InlineSvg::CustomTransformation` and implement the `#transform` method:
125
159
 
@@ -129,10 +163,9 @@ For example, inherit from `InlineSvg::CustomTransformation` and implement the `#
129
163
 
130
164
  class MyCustomTransform < InlineSvg::CustomTransformation
131
165
  def transform(doc)
132
- doc = Nokogiri::XML::Document.parse(doc.to_html)
133
- svg = doc.at_css 'svg'
134
- svg['custom'] = value
135
- doc
166
+ with_svg(doc) do |svg|
167
+ svg["custom"] = value
168
+ end
136
169
  end
137
170
  end
138
171
  ```
@@ -149,7 +182,7 @@ end
149
182
  The custom transformation can then be called like so:
150
183
  ```haml
151
184
  %div
152
- = inline_svg "some-document.svg", my_custom_attribute: 'some value'
185
+ = inline_svg_tag "some-document.svg", my_custom_attribute: 'some value'
153
186
  ```
154
187
 
155
188
  In this example, the following transformation would be applied to a SVG document:
@@ -170,8 +203,8 @@ end
170
203
  The custom transformation will be triggered even if you don't pass any attribute value
171
204
  ```haml
172
205
  %div
173
- = inline_svg "some-document.svg"
174
- = inline_svg "some-document.svg", my_custom_attribute: 'some value'
206
+ = inline_svg_tag "some-document.svg"
207
+ = inline_svg_tag "some-document.svg", my_custom_attribute: 'some value'
175
208
  ```
176
209
 
177
210
  In this example, the following transformation would be applied to a SVG document:
@@ -201,6 +234,92 @@ Transforms are applied in ascending order (lowest number first).
201
234
  ***Note***: Custom transformations are always applied *after* all built-in
202
235
  transformations, regardless of priority.
203
236
 
237
+ ## Custom asset file loader
238
+
239
+ An asset file loader returns a `String` representing a SVG document given a
240
+ filename. Custom asset loaders should be a Ruby object that responds to a
241
+ method called `named`, that takes one argument (a string representing the
242
+ filename of the SVG document).
243
+
244
+ A simple example might look like this:
245
+
246
+ ```ruby
247
+ class MyAssetFileLoader
248
+ def self.named(filename)
249
+ # ... load SVG document however you like
250
+ return "<svg>some document</svg>"
251
+ end
252
+ end
253
+ ```
254
+
255
+ Configure your custom asset file loader in an initializer like so:
256
+
257
+ ```ruby
258
+ InlineSvg.configure do |config|
259
+ config.asset_file = MyAssetFileLoader
260
+ end
261
+ ```
262
+
263
+ ## Caching all assets at boot time
264
+
265
+ When your deployment strategy prevents dynamic asset file loading from disk it
266
+ can be helpful to cache all possible SVG assets in memory at application boot
267
+ time. In this case, you can configure the `InlineSvg::CachedAssetFile` to scan
268
+ any number of paths on disks and load all the assets it finds into memory.
269
+
270
+ For example, in this configuration we load every `*.svg` file found beneath the
271
+ configured paths into memory:
272
+
273
+ ```ruby
274
+ InlineSvg.configure do |config|
275
+ config.asset_file = InlineSvg::CachedAssetFile.new(
276
+ paths: [
277
+ "#{Rails.root}/public/path/to/assets",
278
+ "#{Rails.root}/public/other/path/to/assets"
279
+ ],
280
+ filters: /\.svg/
281
+ )
282
+ end
283
+ ```
284
+
285
+ **Note:** Paths are read recursively, so think about keeping your SVG assets
286
+ restricted to as few paths as possible, and using the filter option to further
287
+ restrict assets to only those likely to be used by `inline_svg_tag`.
288
+
289
+ ## Missing SVG Files
290
+
291
+ If the specified SVG file cannot be found a helpful, empty SVG document is
292
+ embedded into the page instead. The embedded document contains a single comment
293
+ displaying the filename of the SVG image the helper tried to render:
294
+
295
+ ```html
296
+ <svg><!-- SVG file not found: 'some-missing-file.svg' --></svg>
297
+ ```
298
+
299
+ You may apply a class to this empty SVG document by specifying the following
300
+ configuration:
301
+
302
+ ```rb
303
+ InlineSvg.configure do |config|
304
+ config.svg_not_found_css_class = 'svg-not-found'
305
+ end
306
+ ```
307
+
308
+ Which would instead render:
309
+
310
+ ```html
311
+ <svg class='svg-not-found'><!-- SVG file not found: 'some-missing-file.svg' --></svg>
312
+ ```
313
+
314
+ Alternatively, `inline_svg_tag` can be configured to raise an exception when a file
315
+ is not found:
316
+
317
+ ```ruby
318
+ InlineSvg.configure do |config|
319
+ config.raise_on_file_not_found = true
320
+ end
321
+ ```
322
+
204
323
  ## Contributing
205
324
 
206
325
  1. Fork it ( [http://github.com/jamesmartin/inline_svg/fork](http://github.com/jamesmartin/inline_svg/fork) )
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.pattern = Dir.glob("spec/**/*_spec.rb")
6
+ #t.rspec_opts = "--format documentation"
7
+ end
8
+ task :default => :spec
data/inline_svg.gemspec CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "bundler", "~> 2.0"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec", "~> 3.2"
24
24
  spec.add_development_dependency "rspec_junit_formatter", "0.2.2"
25
25
  spec.add_development_dependency "pry"
26
+ spec.add_development_dependency "rubocop"
26
27
 
27
28
  spec.add_runtime_dependency "activesupport", ">= 3.0"
28
29
  spec.add_runtime_dependency "nokogiri", ">= 1.6"
@@ -4,21 +4,82 @@ require 'action_view/context' if defined?(Rails)
4
4
  module InlineSvg
5
5
  module ActionView
6
6
  module Helpers
7
+ def inline_svg_tag(filename, transform_params={})
8
+ with_asset_finder(InlineSvg.configuration.asset_finder) do
9
+ render_inline_svg(filename, transform_params)
10
+ end
11
+ end
12
+
13
+ def inline_svg_pack_tag(filename, transform_params={})
14
+ with_asset_finder(InlineSvg::WebpackAssetFinder) do
15
+ render_inline_svg(filename, transform_params)
16
+ end
17
+ end
18
+
7
19
  def inline_svg(filename, transform_params={})
20
+ render_inline_svg(filename, transform_params)
21
+ end
22
+
23
+ private
24
+
25
+ def backwards_compatible_html_escape(filename)
26
+ # html_escape_once was introduced in newer versions of Rails.
27
+ if ERB::Util.respond_to?(:html_escape_once)
28
+ ERB::Util.html_escape_once(filename)
29
+ else
30
+ ERB::Util.html_escape(filename)
31
+ end
32
+ end
33
+
34
+ def render_inline_svg(filename, transform_params={})
8
35
  begin
9
- svg_file = if InlineSvg::IOResource === filename
10
- InlineSvg::IOResource.read filename
11
- else
12
- InlineSvg::AssetFile.named filename
36
+ svg_file = read_svg(filename)
37
+ rescue InlineSvg::AssetFile::FileNotFound => error
38
+ raise error if InlineSvg.configuration.raise_on_file_not_found?
39
+ return placeholder(filename) unless transform_params[:fallback].present?
40
+
41
+ if transform_params[:fallback].present?
42
+ begin
43
+ svg_file = read_svg(transform_params[:fallback])
44
+ rescue InlineSvg::AssetFile::FileNotFound
45
+ placeholder(filename)
46
+ end
13
47
  end
14
- rescue InlineSvg::AssetFile::FileNotFound
15
- return "<svg><!-- SVG file not found: '#{filename}' #{extension_hint(filename)}--></svg>".html_safe
16
48
  end
17
49
 
18
50
  InlineSvg::TransformPipeline.generate_html_from(svg_file, transform_params).html_safe
19
51
  end
20
52
 
21
- private
53
+ def read_svg(filename)
54
+ if InlineSvg::IOResource === filename
55
+ InlineSvg::IOResource.read filename
56
+ else
57
+ configured_asset_file.named filename
58
+ end
59
+ end
60
+
61
+ def placeholder(filename)
62
+ css_class = InlineSvg.configuration.svg_not_found_css_class
63
+ not_found_message = "'#{backwards_compatible_html_escape(filename)}' #{extension_hint(filename)}"
64
+
65
+ if css_class.nil?
66
+ return "<svg><!-- SVG file not found: #{not_found_message}--></svg>".html_safe
67
+ else
68
+ return "<svg class='#{css_class}'><!-- SVG file not found: #{not_found_message}--></svg>".html_safe
69
+ end
70
+ end
71
+
72
+ def configured_asset_file
73
+ InlineSvg.configuration.asset_file
74
+ end
75
+
76
+ def with_asset_finder(asset_finder)
77
+ Thread.current[:inline_svg_asset_finder] = asset_finder
78
+ output = yield
79
+ Thread.current[:inline_svg_asset_finder] = nil
80
+
81
+ output
82
+ end
22
83
 
23
84
  def extension_hint(filename)
24
85
  filename.ends_with?(".svg") ? "" : "(Try adding .svg to your filename) "
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InlineSvg
4
+ class CachedAssetFile
5
+ attr_reader :assets, :filters, :paths
6
+
7
+ # For each of the given paths, recursively reads each asset and stores its
8
+ # contents alongside the full path to the asset.
9
+ #
10
+ # paths - One or more String representing directories on disk to search
11
+ # for asset files. Note: paths are searched recursively.
12
+ # filters - One or more Strings/Regexps to match assets against. Only
13
+ # assets matching all filters will be cached and available to load.
14
+ # Note: Specifying no filters will cache every file found in
15
+ # paths.
16
+ #
17
+ def initialize(paths: [], filters: [])
18
+ @paths = Array(paths).compact.map { |p| Pathname.new(p) }
19
+ @filters = Array(filters).map { |f| Regexp.new(f) }
20
+ @assets = @paths.reduce({}) { |assets, p| assets.merge(read_assets(assets, p)) }
21
+ @sorted_asset_keys = assets.keys.sort { |a, b| a.size <=> b.size }
22
+ end
23
+
24
+ # Public: Finds the named asset and returns the contents as a string.
25
+ #
26
+ # asset_name - A string representing the name of the asset to load
27
+ #
28
+ # Returns: A String or raises InlineSvg::AssetFile::FileNotFound error
29
+ def named(asset_name)
30
+ assets[key_for_asset(asset_name)] or
31
+ raise InlineSvg::AssetFile::FileNotFound.new("Asset not found: #{asset_name}")
32
+ end
33
+
34
+ private
35
+ # Internal: Finds the key for a given asset name (using a Regex). In the
36
+ # event of an ambiguous asset_name matching multiple assets, this method
37
+ # ranks the matches by their full file path, choosing the shortest (most
38
+ # exact) match over all others.
39
+ #
40
+ # Returns a String representing the key for the named asset or nil if there
41
+ # is no match.
42
+ def key_for_asset(asset_name)
43
+ @sorted_asset_keys.find { |k| k.include?(asset_name) }
44
+ end
45
+
46
+ # Internal: Recursively descends through current_paths reading each file it
47
+ # finds and adding them to the accumulator if the fullpath of the file
48
+ # matches all configured filters.
49
+ #
50
+ # acc - Hash representing the accumulated assets keyed by full path
51
+ # paths - Pathname representing the current node in the directory
52
+ # structure to consider
53
+ #
54
+ # Returns a Hash containing the contents of each asset, keyed by fullpath
55
+ # to the asset.
56
+ def read_assets(acc, paths)
57
+ paths.each_child do |child|
58
+ if child.directory?
59
+ read_assets(acc, child)
60
+ elsif child.readable_real?
61
+ acc[child.to_s] = File.read(child) if matches_all_filters?(child)
62
+ end
63
+ end
64
+ acc
65
+ end
66
+
67
+ def matches_all_filters?(path)
68
+ filters.all? { |f| f.match(path.to_s) }
69
+ end
70
+ end
71
+ end
@@ -6,7 +6,7 @@ module InlineSvg
6
6
  end
7
7
 
8
8
  def self.configured_asset_finder
9
- InlineSvg.configuration.asset_finder
9
+ Thread.current[:inline_svg_asset_finder] || InlineSvg.configuration.asset_finder
10
10
  end
11
11
  end
12
12
  end
@@ -1,8 +1,17 @@
1
+ require 'digest'
2
+
1
3
  module InlineSvg
2
4
  class IdGenerator
3
- def self.generate(base, salt)
4
- bytes = Digest::SHA1.digest("#{base}-#{salt}")
5
- Digest.hexencode(bytes).to_i(16).to_s(36)
5
+ class Randomness
6
+ require "securerandom"
7
+ def self.call
8
+ SecureRandom.hex(10)
9
+ end
10
+ end
11
+
12
+ def self.generate(base, salt, randomness: Randomness)
13
+ bytes = Digest::SHA1.digest("#{base}-#{salt}-#{randomness.call}")
14
+ 'a' + Digest.hexencode(bytes).to_i(16).to_s(36)
6
15
  end
7
16
  end
8
17
  end