themizer 0.9

Sign up to get free protection for your applications and to get access to all the features.
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: []