eex2slime 0.3.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1beaf57748184f55101b8b74c57b4a2543b2ba65
4
- data.tar.gz: 9debc17d21b9e7c7cb0659d0857a916082cff1e1
3
+ metadata.gz: 36918c220cf3167b9e2ee99ed463046f11c210bd
4
+ data.tar.gz: 34b61cc5e0e804843b428b1162f5bb579e0b4f85
5
5
  SHA512:
6
- metadata.gz: e4a18eb89292a887c46d93544bccafe0f1e44396dc3076f964de7fb7f84c4a686158cf8b9078355462eb9535bf362c1bcbccf52e9b98105d940326dbea4313b2
7
- data.tar.gz: 7a39ad41e70f475b8185f08af92e8cdab386076f80e3aeb1dc4e3ab209028684bc1903c774749cdcc3573704e7f3ce1e86975cf80cd2696bc534f50240df2601
6
+ metadata.gz: dd509471dbf78b58e891dae7b604761931e7c30ad60cbff8ea24812ebf228174bc6970888345ef02a621ea0db90f23350dd05e0fc9e9001342c2522da313ba91
7
+ data.tar.gz: c606b14331370634cd4e32590a16fbc9a86f1685356f06b08914ec73401e0002063d3fd205e74af37537eb9943e6d7e49d217ce139a7e1ac28b11b8922ae22ba
data/README.md CHANGED
@@ -1,20 +1,32 @@
1
1
  ## EEx2Slime
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/eex2slime.svg)](https://badge.fury.io/rb/eex2slime)
4
+ [![Build Status](https://travis-ci.org/hedgesky/eex2slime.svg?branch=master)](https://travis-ci.org/hedgesky/eex2slime)
5
+
3
6
  Script for converting EEx templates to [Slime](http://slime-lang.com). Slime is a lightweight template language.
4
7
 
5
8
  ## Usage
6
9
 
7
- You may convert files using the included executable `eex2slime`.
10
+ You may convert files using the included executable `eex2slime`:
11
+
12
+ ```bash
13
+ $ gem install eex2slime
8
14
 
9
- # eex2slime -h
15
+ $ eex2slime foo.html.eex # outputs to foo.html.slime by default
16
+ $ eex2slime foo.html.eex bar.html.slime # outputs to bar.html.slime
17
+ $ eex2slime foo.html.eex - # outputs to stdout
18
+ $ cat foo.eex | eex2slime # input from stdin, outputs to stdout
19
+ $ eex2slime dir/ # convert all .eex files recursively
20
+ $ eex2slime --delete dir/ # delete .eex files after convertion. Be sure you have a backup!
21
+ ```
10
22
 
11
- Usage: eex2slime INPUT_FILENAME_OR_DIRECTORY [OUTPUT_FILENAME_OR_DIRECTORY] [options]
12
- --trace Show a full traceback on error
13
- -d, --delete Delete EEx files
14
- -h, --help Show this message
15
- -v, --version Print version
23
+ Alternatively you could use the following API:
16
24
 
17
- Alternatively, to convert files or strings on the fly in your application, you may do so by calling `EEx2Slime.convert!(file)`.
25
+ ```ruby
26
+ require 'eex2slime'
27
+ EEx2Slime.convert('path/to/file')
28
+ EEx2Slime.convert_string('<nav class="navbar"></nav>')
29
+ ```
18
30
 
19
31
  ## Installation
20
32
 
@@ -23,3 +35,41 @@ Alternatively, to convert files or strings on the fly in your application, you m
23
35
  ## Regards
24
36
 
25
37
  Huge thanks to [Maiz Lulkin](https://github.com/joaomilho) and his original [html2slim repo](https://github.com/slim-template/html2slim).
38
+
39
+ ## Does it really work?
40
+
41
+ It might fail in some cases, but in general yes, it does! I've checked it on the opersourced [changelog.com app](https://github.com/thechangelog/changelog.com). After a bit of preparing this tool finely converted all EEx templates.
42
+
43
+ CI runs tests on Rubies 2.2, 1.9.3. Ruby 1.8.7 isn't supported.
44
+
45
+ ## Known issues
46
+
47
+ - Incorrect HTML will break inner HTML parser. Example (notice misplaced slash):
48
+
49
+ ```html
50
+ <img width="75" / height="75">
51
+ ```
52
+
53
+ - Nested interpolation won't play well with Slime. This:
54
+
55
+ ```erb
56
+ <img src="<%= static_url(@conn, "/images/podcasts/#{@podcast.slug}.svg") %>">
57
+ ```
58
+
59
+ should be rewritten to:
60
+
61
+ ```erb
62
+ <% image_url = static_url(@conn, "/images/podcasts/#{@podcast.slug}.svg") %>
63
+ <img src="<%= image_url %>">
64
+ ```
65
+
66
+ - This library doesn't support inline `if`'s interpolation:
67
+
68
+ ```erb
69
+ <!-- such constructions aren't supported -->
70
+ <article class="<%= if index == 0 do %>is-active<% end %>"></article>
71
+ ```
72
+
73
+ ## License
74
+
75
+ This project uses MIT license.
@@ -1,3 +1,5 @@
1
+ # This class parses CLI arguments and runs converter.
2
+
1
3
  require 'optparse'
2
4
  require 'eex2slime'
3
5
 
@@ -14,10 +16,10 @@ module EEx2Slime
14
16
  @opts.parse!(@args)
15
17
  process!
16
18
  exit 0
17
- rescue Exception => ex
18
- raise ex if @options[:trace] || SystemExit === ex
19
- $stderr.print "#{ex.class}: " if ex.class != RuntimeError
20
- $stderr.puts ex.message
19
+ rescue => error
20
+ raise error if @options[:trace] || SystemExit === error
21
+ $stderr.print "#{error.class}: " if error.class != RuntimeError
22
+ $stderr.puts error.message
21
23
  $stderr.puts ' Use --trace for backtrace.'
22
24
  exit 1
23
25
  end
@@ -102,7 +104,7 @@ module EEx2Slime
102
104
  else
103
105
  $stdout
104
106
  end
105
- @options[:output].puts EEx2Slime.convert!(in_file)
107
+ @options[:output].puts EEx2Slime.convert(in_file)
106
108
  @options[:output].close
107
109
 
108
110
  File.delete(file) if @options[:delete]
@@ -1,7 +1,9 @@
1
- require_relative 'hpricot_monkeypatches'
1
+ # This class takes EEx string and replaces all embedded elixir with
2
+ # <elixir code="...">...</elixir> tags, which are parsed with Hpricot.
3
+ require_relative "hpricot_monkeypatches"
2
4
 
3
5
  module EEx2Slime
4
- class EExConverter
6
+ class Converter
5
7
  def self.from_stream(stream)
6
8
  input =
7
9
  if stream.is_a?(IO)
@@ -19,8 +21,8 @@ module EEx2Slime
19
21
  prepare_else_statements!
20
22
  prepare_elixir_condition_expressions!
21
23
  prepare_end_statements!
22
- prepare_regular_elixir_code!
23
24
  prepare_elixir_inside_attributes!
25
+ prepare_regular_elixir_code!
24
26
  @slime = Hpricot(@eex).to_slime
25
27
  end
26
28
 
@@ -30,16 +32,11 @@ module EEx2Slime
30
32
 
31
33
  private
32
34
 
33
- def prepare_curly_blocks!
34
- @eex.gsub!(/<%(.+?)\s*\{\s*(\|.+?\|)?\s*%>/) {
35
- %(<%#{$1} do #{$2}%>)
36
- }
37
- end
38
-
39
35
  def prepare_control_flow_statements!
40
- @eex.gsub!(/<%(-\s+)?((\s*(case|if|for|unless) .+?)|.+?do\s*(\|.+?\|)?\s*)-?%>/) {
41
- %(<elixir code="#{$2.gsub(/"/, '&quot;')}">)
42
- }
36
+ regex1 = /<%(?:-\s+)?((\s*(case|if|for|unless) ((?:(?!%>).)+)?))-?%>/
37
+ regex2 = /<%(?:-\s+)?(((?:(?!%>).)+)?do\s*(\|.+?\|)?\s*)-?%>/
38
+ @eex.gsub!(regex1) { %(<elixir code="#{$1.gsub(/"/, '&quot;')}">) }
39
+ @eex.gsub!(regex2) { %(<elixir code="#{$1.gsub(/"/, '&quot;')}">) }
43
40
  end
44
41
 
45
42
  def prepare_elixir_anonymous_functions!
@@ -62,38 +59,37 @@ module EEx2Slime
62
59
  @eex.gsub!(/<%=?\s*(end|}|end\s+-)\s*%>/, %(</elixir>))
63
60
  end
64
61
 
65
- def prepare_regular_elixir_code!
66
- @eex.gsub!(/<%-?(.+?)\s*-?%>/m) {
67
- %(<elixir code="#{$1.gsub(/"/, '&quot;')}"></elixir>)
68
- }
69
- end
70
-
71
- # test string
72
- # <div class="form <%= a() %>" data="<%= b() %>"></div>
62
+ # test string:
63
+ # <div class="form <%= a() %> " data="<%= b() %> another"></div>
64
+ # <%= another() %>
73
65
  def prepare_elixir_inside_attributes!
74
- # /
75
- # =" # ensure we are in attribute value
76
- # ([^"]*) # match possible other values
77
- # (
78
- # <elixir code="= ?
79
- # ( # match all code inside elixir tag
80
- # (?:.(?!elixir))+ # but forbid spanning over other attributes
81
- # )
82
- # "><\/elixir>
83
- # )
84
- # /x
66
+ # =" ensure we are inside attributes
67
+ # ([^"]*) capture other attributes
68
+ # <%= ensure we are inside outputting elixir tag
69
+ # ((?:(?!%>).)+) capture code, don't span across multiple elixir tags
70
+ # \s* swallow spaces
71
+ # -?%> end of elixir tag
85
72
  #
86
73
  # Example match data:
87
74
  # Match 1
88
75
  # 1. form
89
- # 2. <elixir code="= a()"></elixir>
90
- # 3. a()
76
+ # 2. a()
91
77
  # Match 2
92
78
  # 1.
93
- # 2. <elixir code="= b()"></elixir>
94
- # 3. b()
95
- regex = /="([^"]*)(<elixir code="= ?((?:.(?!elixir))+)"><\/elixir>)/
96
- @eex.gsub!(regex, '="\\1#{\\3}')
79
+ # 2. b()
80
+ regex = /="([^"]*)<%=((?:(?!%>).)+)\s*-?%>/
81
+ @eex.gsub!(regex) {
82
+ # use variables, because gsub changes $1 and $2 values
83
+ attrs = $1
84
+ elixir = $2.gsub('"', "&quot;").strip
85
+ %Q(="#{attrs}\#{#{elixir}})
86
+ }
87
+ end
88
+
89
+ def prepare_regular_elixir_code!
90
+ @eex.gsub!(/<%-?(.+?)\s*-?%>/m) {
91
+ %(<elixir code="#{$1.gsub(/"/, '&quot;')}"></elixir>)
92
+ }
97
93
  end
98
94
  end
99
95
  end
@@ -1,3 +1,4 @@
1
+ # Patch Hpricot to support our custom <elixir> tags.
1
2
  require 'hpricot'
2
3
 
3
4
  Hpricot::XHTMLTransitional.tagset[:elixir] = [:code]
@@ -83,12 +84,12 @@ class Hpricot::Elem
83
84
  end
84
85
  end
85
86
 
87
+ private
88
+
86
89
  def elixir?
87
90
  name == "elixir"
88
91
  end
89
92
 
90
- private
91
-
92
93
  def children_slime(lvl)
93
94
  children
94
95
  .map { |c| c.to_slime(lvl+1) }
@@ -130,7 +131,8 @@ class Hpricot::Elem
130
131
  def slime_attributes
131
132
  remove_css_class
132
133
  remove_attribute('id')
133
- has_attributes? ? "[#{attributes_as_html.to_s.strip}]" : ""
134
+ return "" unless has_attributes?
135
+ "[#{attrs_with_restored_interpolation}]"
134
136
  end
135
137
 
136
138
  def has_attributes?
@@ -149,26 +151,47 @@ class Hpricot::Elem
149
151
  name == "div"
150
152
  end
151
153
 
154
+ def attrs_with_restored_interpolation
155
+ regex = /\#{([^{}]+)}/
156
+ attributes_as_html.to_s.strip.gsub(regex) do
157
+ code = $1.gsub("&quot;", '"')
158
+ "\#{ #{code} }"
159
+ end
160
+ end
161
+
152
162
  def remove_css_class
153
163
  if has_class? && cryptic_classes.any?
154
164
  self["class"] = cryptic_classes.join(" ")
155
165
  else
156
- remove_attribute('class')
166
+ remove_attribute("class")
157
167
  end
158
168
  end
159
169
 
160
170
  def non_cryptic_classes
161
- return [] unless has_attribute?('class')
162
- classes = self['class'].strip.split(/\s+/)
163
- classes.reject { |s| s.match(/[=#]/) }
171
+ crypto_analyzer.last
172
+ end
173
+
174
+ def cryptic_classes
175
+ crypto_analyzer.first
164
176
  end
165
177
 
166
178
  # We can have interpolation inside attributes.
167
179
  # Such classes should always be in attributes section (not shortened)
168
- def cryptic_classes
169
- return [] unless has_attribute?('class')
170
- classes = self['class'].strip.split(/\s+/)
171
- classes.select { |s| s.match(/[=#]/) }
180
+ # This handles cituations like this:
181
+ # <div class="form foo-<%= error_class f, :slug %>-bar"></div>
182
+ def crypto_analyzer
183
+ return [[], []] unless has_attribute?("class")
184
+ @crypto_analyzer ||= begin
185
+ class_value = self["class"].strip
186
+ interpolation_regex = /[-\w]*\#{(?:[^{}]+)}[-\w]*/
187
+ interpolated_classes = class_value.scan(interpolation_regex)
188
+ class_value.gsub!(interpolation_regex, "")
189
+
190
+ crypt, non_crypt = class_value.split(/\s+/).partition do |klass|
191
+ klass.match(/[=#"&()]/)
192
+ end
193
+ [crypt + interpolated_classes, non_crypt]
194
+ end
172
195
  end
173
196
  end
174
197
 
@@ -1,3 +1,3 @@
1
1
  module EEx2Slime
2
- VERSION = '0.3.0'
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/eex2slime.rb CHANGED
@@ -1,8 +1,15 @@
1
+ # This library converts EEx templates to Slime. It's based on html2slim
2
+ # library by @joaomilho. Usage examples are in README.
3
+
1
4
  require_relative 'eex2slime/version'
2
5
  require_relative 'eex2slime/converter'
3
6
 
4
7
  module EEx2Slime
5
- def self.convert!(input)
6
- EExConverter.from_stream(input).to_s
8
+ def self.convert(input)
9
+ Converter.from_stream(input).to_s
10
+ end
11
+
12
+ def self.convert_string(eex)
13
+ Converter.new(eex).to_s
7
14
  end
8
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eex2slime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Chuchkalov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-08 00:00:00.000000000 Z
11
+ date: 2017-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hpricot
@@ -81,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
- version: '0'
84
+ version: 1.9.3
85
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="