themizer 0.9

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/themizer.rb +177 -0
  3. metadata +59 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3372958d6e65407edac2be3db48203563f25a8e4
4
+ data.tar.gz: 7812ede3d4f58bafdb03037f36f0623d68178a4e
5
+ SHA512:
6
+ metadata.gz: b86627d3296fbda93dbd41bc64033aec29aa06fe33ee7913aaeb01e1925a994779cf0c128e054df3c1e53034c8ebb3a0fac671e998f5f82d2cb1dfd1afca7618
7
+ data.tar.gz: 2e09d4261849854e4e74e65fc6f8af4f3eb344d0bcd6f41be6f3937778de22890b52a2f8a4a2da79ea71ad078450f3b0e3310e0657d4f2f50e32bb482dca7d29
data/lib/themizer.rb ADDED
@@ -0,0 +1,177 @@
1
+ module Themizer
2
+ ARGUMENT_ERROR =
3
+ "Themizer: argument error in 'init(args)': "\
4
+ "args should be Hash, "\
5
+ "args should contain ':themes' key, "\
6
+ "args[:themes] should be Array of strings"
7
+
8
+ def init(args)
9
+ raise ARGUMENT_ERROR unless args.class == Hash
10
+ raise ARGUMENT_ERROR unless args[:themes]
11
+ raise ARGUMENT_ERROR unless args[:themes].class == Array
12
+ raise ARGUMENT_ERROR unless args[:themes].map { |e| e.to_s } == args[:themes]
13
+ @themes = args[:themes]
14
+ @debug = args[:debug] || false
15
+ end
16
+
17
+ def themize(action = :expand, theme = "\"\"", &block)
18
+ return unless block_given?
19
+ src_erb = _extract_block(block)
20
+ return unless src_erb
21
+ _debug(src_erb, "processing block")
22
+ src_sass = ERB.new(src_erb).result
23
+
24
+ case action
25
+ when :expand
26
+ maps, selectors = _mapize(src_sass)
27
+ src_sass_expanded = maps
28
+ unless selectors.strip.empty?
29
+ # if you add "\"\"" to @themes, sass won't compile
30
+ # because it cannot iterate through list with empty strings in it
31
+ # therefore, you have to process unthemed sass separately
32
+ src_sass_expanded << _expand_tilda(selectors, "\"\"")
33
+
34
+ # apply & sass operator
35
+ #
36
+ # ".form {
37
+ # ...
38
+ # }"
39
+ # =>
40
+ # "@each $theme in theme1, theme2 {
41
+ # #{$theme} { &
42
+ # ...
43
+ # }
44
+ # }"
45
+ src_sass_expanded << "\n@each $theme in #{@themes.join(', ')} {"
46
+ src_sass_expanded << ".\#{$theme} { & "
47
+ src_sass_expanded << _expand_tilda(selectors)
48
+ src_sass_expanded << "}}"
49
+ @themes.each do |theme|
50
+ src_sass_expanded.gsub!(".#{theme} body", "body.#{theme}")
51
+ end
52
+ end
53
+
54
+ when :contract
55
+ src_sass_expanded = _contract_tilda(src_sass, theme)
56
+ end
57
+
58
+ _debug(src_sass_expanded, "resulting sass")
59
+
60
+ yield.gsub!(src_sass, src_sass_expanded)
61
+ end
62
+
63
+ def unthemize(theme = "\"\"", &block)
64
+ themize(:contract, theme, &block)
65
+ end
66
+
67
+ # naive attempt to extract "themize do" block source
68
+ # considering there are probably other "do ... end" blocks inside;
69
+ def _extract_block(block)
70
+ src_file_path = block.source_location[0]
71
+ block_start_line_number = block.source_location[1]
72
+
73
+ src_file_content = File.open(src_file_path).read
74
+ lines_after_block_start = src_file_content.split("\n")[block_start_line_number..-1]
75
+
76
+ # block already starts with one "do"
77
+ do_counter = 1
78
+ # assuming that "do themize" and corresponding "end" placed on separate lines
79
+ block_end_index = 1
80
+ lines_after_block_start.each_with_index do |line, i|
81
+ # http://stackoverflow.com/a/11899069/6376451
82
+ do_counter += line.scan(/<%.*?do.*?%>/).size
83
+ do_counter -= line.scan(/<%.*?end.*?%>/).size
84
+ if line =~ /\s*<%\s*end\s*%>/ && do_counter == 0
85
+ block_end_index = i
86
+ break
87
+ end
88
+ end
89
+ return nil if block_end_index == 0
90
+
91
+ lines_after_block_start[0..block_end_index-1].join("\n")
92
+ end
93
+
94
+ SASS_VAR_TILDA_IMPORTANT = /(?<sass_var>\$[^\s:~;]*)~(?<important>[^;]*);/
95
+
96
+ # expant tilda to if ... map-get
97
+ #
98
+ # "color: $form-color~;"
99
+ # =>
100
+ # "color: if(map-has-key($form-color, $theme), map-get($form-color, $theme), map-get($form-color, default));"
101
+ def _expand_tilda(sass, theme = "$theme", ending = ";")
102
+ # http://stackoverflow.com/a/28982676/6376451
103
+ sass.gsub(
104
+ SASS_VAR_TILDA_IMPORTANT,
105
+ "if("\
106
+ "map-has-key(\\k<sass_var>, #{theme}), "\
107
+ "map-get(\\k<sass_var>, #{theme}), "\
108
+ "map-get(\\k<sass_var>, \"\")"\
109
+ ")\\k<important>#{ending}"
110
+ )
111
+ end
112
+
113
+ # reduce tilda to single map-get
114
+ #
115
+ # color: $form-color~;
116
+ # =>
117
+ # color: map-get($form-color, "\"\"");
118
+ def _contract_tilda(sass, theme)
119
+ sass.gsub(
120
+ SASS_VAR_TILDA_IMPORTANT,
121
+ "map-get(\\k<sass_var>, #{theme})\\k<important>;"
122
+ )
123
+ end
124
+
125
+ # expand sass vars denifitions with tildas into maps
126
+ #
127
+ # <% themize %>
128
+ # $table-border: $table-border-thickness solid $white~;
129
+ # .someclass { color: $table-border~; }
130
+ # <% end %>
131
+ # =>
132
+ # [
133
+ # "$table-border: (
134
+ # default: $table-border-thickness solid map-get($white, default);
135
+ # dark: $table-border-thickness solid map-get($white, dark);
136
+ # contrast: $table-border-thickness solid map-get($white, contrast);
137
+ # );",
138
+ # ".someclass { color: $table-border~; }"
139
+ # ]
140
+ def _mapize(sass)
141
+ maps = ""
142
+
143
+ # http://stackoverflow.com/a/18089658/6376451
144
+ expressions = sass.split(/(?<=[;])/).map { |e| e.strip }
145
+ themes_expanded = ["\"\""] + @themes
146
+ expressions.each_with_index do |expression, i|
147
+ if expression =~ /^(?<map_name>\$.*):(?<map_value>.*;)/
148
+ maps << "#{Regexp.last_match(:map_name)}: ("
149
+ themes_expanded.each do |theme|
150
+ maps << "#{theme}: #{_expand_tilda(Regexp.last_match(:map_value), theme, ',')}"
151
+ end
152
+ maps << ");"
153
+ expressions[i].clear
154
+ end
155
+ end
156
+ selectors = expressions
157
+ .map { |e| e.strip }
158
+ .reject { |e| e.empty? }
159
+ .join("\n")
160
+
161
+ [maps, selectors]
162
+ end
163
+
164
+ def _debug(message, description)
165
+ if @debug
166
+ puts "\n##########################################"
167
+ puts "# begin Themizer #{description}"
168
+ puts "\n\n#{message}\n\n"
169
+ puts "# end Themizer #{description}"
170
+ puts "##########################################\n"
171
+ end
172
+ end
173
+
174
+ module_function :init, :themize, :unthemize,
175
+ :_mapize, :_expand_tilda, :_contract_tilda, :_extract_block, :_debug
176
+ private_class_method :_mapize, :_expand_tilda, :_contract_tilda, :_extract_block, :_debug
177
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: themizer
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.9'
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Morozov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby_expect
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ description: Provides methods which expands you regular SASS in rails into themed
28
+ one using & operator and other cool SASS features.
29
+ email: ntcomp12@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/themizer.rb
35
+ homepage: https://github.com/kengho/themizer
36
+ licenses:
37
+ - MIT
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.5.2
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Gem for producing themed css from scss.erb
59
+ test_files: []