saag 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,24 @@
1
+ 2009-08-08 sugamasao@gmail.com
2
+
3
+ * Version 0.3.5 : add option "-x"
4
+
5
+ 2009-07-06 sugamasao@gmail.com
6
+
7
+ * Version 0.3.4 : modified ends at the [Sass Syntax Error] or [File I/O
8
+ Error]. endless loop for file search.
9
+
10
+ 2009-07-05 sugamasao@gmail.com
11
+
12
+ * Version 0.3.2 : moved README to README.rdoc
13
+ * Version 0.3.0 : added to rdoc comments
14
+
15
+ 2009-06-25 sugamasao@gmail.com
16
+
17
+ * Version 0.2.4 : change to this file.
18
+ * Version 0.2.3 : change to Version Number
19
+ * Version 0.2.2 : Bug Fix to issue 1
20
+
21
+ 2009-06-21 sugamasao@gmail.com
22
+
23
+ * Version 0.1.0 : 1st Release
24
+
data/README.rdoc ADDED
@@ -0,0 +1,70 @@
1
+ = saag
2
+
3
+ == Description
4
+
5
+ SASS automatic monitor and generate CSS file.
6
+
7
+ SASS ファイルを監視し、変更があれば即座に CSS ファイルへ変換します。
8
+
9
+ 手動で以下のコマンドを行うのと同等です。
10
+ % sass hoge.sass hoge.css
11
+
12
+ == Gem Installation
13
+
14
+ gem install sugamasao-saag --source http://gems.github.com
15
+
16
+ == Features/Problems
17
+
18
+ * Ruby 1.8.6 及び、 1.8.7 で、動作確認を行っています
19
+ * Windows XP 及び、 Mac OSX で動作確認を行っています
20
+ * 事前にSass(HAML)のインストールが必要になります(未インストール時は本gemと一緒にインストールされます)
21
+
22
+ == Synopsis
23
+
24
+ 使用例:
25
+
26
+ === オプション無し
27
+ % saag
28
+
29
+ 引数を使用しない場合、コマンド実行者のカレントディレクトリを基点とし、*.sass ファイルを探し、対象ファイルとします。
30
+
31
+ また、出力される CSS ファイルは sass ファイルと同じ階層になります。
32
+
33
+ (Version 0.3.5以降) saag を実行するよりも以前のタイムスタンプのファイルは対象ファイルに含まれません 。
34
+
35
+ 古いタイムスタンプも変換したい場合は -x オプションを使用してください
36
+
37
+ === オプション -i
38
+ % saag -i /path/to/sass
39
+
40
+ -i オプションを使用することで、sass ファイルのあるディレクトリ(ファイル名でも可)を指定する事が可能です。
41
+
42
+ 出力先は、変更対象の sass ファイルのあるディレクトリと同じディレクトリになります。
43
+
44
+ === オプション -o
45
+ % saag -i /path/to/sass -o /path/to/css
46
+
47
+ -o オプションを使用することで、css の出力先ファイルを指定することができます。-o オプション単独での使用も可能です。
48
+
49
+ 出力先ファイルのディレクトリ構成は、-i オプションに依存します。
50
+
51
+ 上記のオプションを指定、/path/to/sass/sub_dir/hoge.sass があった場合、出力先ディレクトリは
52
+
53
+ /path/to/css/sub_dir/hoge.css となります。
54
+
55
+ -o 単独で使用した場合、実行時のカレントディレクトリ配下から *.sass ファイルを探し、 -o で指定したディレクトリに *.css を出力します。
56
+
57
+ === オプション -x
58
+ % saag -x
59
+
60
+ -x オプションを使用する事で、本プロセスを起動するよりも前に編集された sass ファイルを変更対象に含めます。
61
+
62
+ 逆に言うと、 -x を使用しない場合は、saag 起動後に編集したファイルしか変更対象になりません。
63
+
64
+ このオプションは Version 0.3.5 以降で実装されました。
65
+
66
+ == Copyright
67
+
68
+ Author:: sugamasao <sugamasao@gmail.com>
69
+ License:: Ruby's
70
+
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ = TODO List
2
+
3
+ * GUI Interface
4
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.5
data/bin/saag ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # $Id$
3
+
4
+ # lib のパスを追加しておく
5
+ $LOAD_PATH.unshift(File.expand_path("#{File.dirname(__FILE__)}/../lib/"))
6
+
7
+ begin
8
+ require 'saag'
9
+ require 'sass'
10
+ rescue LoadError
11
+ require 'rubygems'
12
+ require 'saag'
13
+ require 'sass'
14
+ end
15
+
16
+ Saag.new(ARGV).run
17
+
data/lib/saag.rb ADDED
@@ -0,0 +1,453 @@
1
+ require 'optparse'
2
+ require 'logger'
3
+ require 'pp'
4
+
5
+ #
6
+ # Sass 補助ツール、saag クラスです
7
+ #
8
+ class Saag
9
+ SASS_EXT = '.sass'
10
+ CSS_EXT = '.css'
11
+
12
+ #== saag Constructor
13
+ # 引数の解析、及び初期値の設定を行います
14
+ #
15
+ #=== 引数
16
+ # 引数となる配列
17
+ #
18
+ #=== 例外
19
+ # 特になし
20
+ #
21
+ def initialize(argv = [])
22
+ # クラス共通の値
23
+ @app_name = self.class
24
+ begin
25
+ @version = File.read("#{File.dirname(__FILE__)}/../VERSION").chomp
26
+ rescue => e
27
+ @version = "0.0.0"
28
+ end
29
+
30
+ # 引数オプション格納用
31
+ @conf = {}
32
+
33
+ # シグナル受信用フラグ(シグナルを受けたら true にして終了する)
34
+ @exit = false
35
+
36
+ # Loger の設定
37
+ @logger = Logger.new(STDOUT)
38
+ @logger.level = Logger::INFO # default Log Level
39
+
40
+ begin
41
+ OptionParser.new do |opt|
42
+ opt.on('-i', '--input_path=VAL', 'input file path(directory or filename)') do |v|
43
+ @conf[:in_path] = set_dir_path(v)
44
+ end
45
+ opt.on('-o', '--output_path=VAL', 'generated css file output path') do |v|
46
+ @conf[:out_path] = set_dir_path(v)
47
+ end
48
+ opt.on('-r', '--render_opt=VAL', 'sass render option [nested or expanded or compact or compressed]') do |v|
49
+ @conf[:render_opt] = set_render_opt(v)
50
+ end
51
+ opt.on('-v', '--version', 'show version' ) do
52
+ # TODO stderror print.
53
+ puts "#{@app_name} #{@version}";
54
+ exit 1
55
+ end
56
+ opt.on('-d', '--debug', 'log level to debug') do |v|
57
+ @conf[:debug] = v
58
+ end
59
+ opt.on('-x', '--exclude', 'exclude older Sass Script(target to "Time.now < Sass Script Modified Time")') do
60
+ @conf[:exclude] = true
61
+ end
62
+ end.parse!(argv)
63
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
64
+ @logger.error('invalid args...! looks for "-h" option')
65
+ exit 1
66
+ end
67
+
68
+ @logger.info("sass file watching start... [#{@app_name} = #{@version}]")
69
+ @logger.level = Logger::DEBUG if @conf[:debug]
70
+
71
+ @logger.debug("args input_path => #{@conf[:in_path]}")
72
+ @logger.debug("args output_path => #{@conf[:out_path]}")
73
+ @logger.debug("args render_opt => #{@conf[:render_opt]}")
74
+ @logger.debug("args -x? => #{@conf[:exclude]}")
75
+
76
+ set_default_conf()
77
+ set_signal()
78
+
79
+ @logger.info("watching directory => #{@conf[:in_path]}")
80
+ @logger.info("output directory => #{@conf[:out_path]}")
81
+ end
82
+
83
+ #== saag#run
84
+ # saag によるファイル監視を実行します
85
+ #
86
+ #=== 引数
87
+ # なし
88
+ #
89
+ #=== 戻り値
90
+ # なし
91
+ #
92
+ #=== 例外
93
+ # なし
94
+ #
95
+ def run
96
+ old_list = []
97
+ # メインループ
98
+ begin
99
+ loop do
100
+ old_list = main_loop(old_list)
101
+ break if @exit # SIGNAL を受けたりすると true
102
+ sleep 1
103
+ end
104
+ rescue => e
105
+ @logger.error("FATLE Error! [#{e.message}]")
106
+ end
107
+
108
+ @logger.info("sass file watching exit...")
109
+ end
110
+
111
+ ############################
112
+ # private methods
113
+ ############################
114
+ private
115
+
116
+ #== saag#main_loop
117
+ # saag の監視処理のメイン処理です。
118
+ # このメソッドを繰り返し呼ぶ事で監視処理を実施しています。
119
+ #
120
+ #=== 引数
121
+ # _old_list_:: Array オブジェクト 前回の main_loop メソッドで取得したファイルリストの一覧。
122
+ #
123
+ #=== 戻り値
124
+ # Array:: main_loop内で取得したファイルリストの配列。
125
+ #
126
+ #=== 例外
127
+ # なし
128
+ #
129
+ def main_loop(old_list)
130
+ return nil if @exit # SIGNAL を受けていたら処理を抜ける
131
+ new_list = get_file_list(@conf[:in_path])
132
+
133
+ return nil if @exit # SIGNAL を受けていたら処理を抜ける
134
+ file_list = check_file_list(old_list, new_list)
135
+
136
+ return nil if @exit # SIGNAL を受けていたら処理を抜ける
137
+ file_list.each do |sass_file|
138
+ if sass_file[:change]
139
+ @logger.info("change file found. => #{sass_file[:path]}")
140
+ begin
141
+ # render to sass text
142
+ css_text = Sass::Engine.new(File.read(sass_file[:path]), {:style => @conf[:render_opt] }).render
143
+ write_css_file(sass_file, css_text)
144
+ rescue Sass::SyntaxError => e
145
+ @logger.error("Sass Syntax Error! file => [#{sass_file[:path]}] error message => [#{e.message}]")
146
+ new_list = set_default_time(new_list, sass_file[:path])
147
+ rescue SystemCallError => e
148
+ @logger.error("File I/O Error! file => [#{sass_file[:path]}] error message => [#{e.message}]")
149
+ new_list = set_default_time(new_list, sass_file[:path])
150
+ end
151
+ end
152
+ end
153
+
154
+ return new_list
155
+ end
156
+
157
+ #== saag#set_dir_path
158
+ # 絶対パスにし、ディレクトリの場合は最後尾にスラッシュを付ける
159
+ #
160
+ #=== 引数
161
+ # _path_:: ファイルパスの記載された String オブジェクト
162
+ #
163
+ #=== 戻り値
164
+ # String:: ディレクトリであれば末尾に '/' を付加した、絶対パスのファイル名
165
+ #
166
+ #=== 例外
167
+ # なし
168
+ #
169
+ def set_dir_path(path)
170
+ path = File.expand_path(path)
171
+ path = path + '/' if File.directory?(path)
172
+ return path
173
+ end
174
+
175
+ #== saag#set_signal
176
+ # シグナルハンドラの設定
177
+ # シグナルを受けると即死するのではなく、キリの良いところで終了するよう、
178
+ # 終了フラグを true にするだけの処理を行う。
179
+ #
180
+ #=== 引数
181
+ # なし
182
+ #
183
+ #=== 戻り値
184
+ # なし
185
+ #
186
+ #=== 例外
187
+ # なし
188
+ #
189
+ def set_signal
190
+ begin
191
+ Signal.trap(:INT){
192
+ @logger.debug("Signal Trapping [:INT]")
193
+ @exit = true
194
+ }
195
+ rescue ArgumentError => e
196
+ @logger.debug("Signal Setting Error[#{e.message}]")
197
+ end
198
+
199
+ begin
200
+ Signal.trap(:TERM){
201
+ @logger.debug("Signal Trapping [:TERM]")
202
+ @exit = true
203
+ }
204
+ rescue ArgumentError => e
205
+ @logger.debug("Signal Setting Error[#{e.message}]")
206
+ end
207
+
208
+ begin
209
+ Signal.trap(:HUP){
210
+ @logger.debug("Signal Trapping [:HUP]")
211
+ @exit = true
212
+ }
213
+ rescue ArgumentError => e
214
+ @logger.debug("Signal Setting Error[#{e.message}]")
215
+ end
216
+
217
+ begin
218
+ Signal.trap(:BREAK){
219
+ @logger.debug("Signal Trapping [:BREAK]")
220
+ @exit = true
221
+ }
222
+ rescue ArgumentError => e
223
+ @logger.debug("Signal Setting Error[#{e.message}]")
224
+ end
225
+ end
226
+
227
+ #== saag#set_render_opt
228
+ # sass 実行時のオプション設定用メソッド
229
+ # オプションで値が渡されていれば その値を、そうでなければでフォルト値のnestedを使う。
230
+ #
231
+ #=== 引数
232
+ # _input_opt_:: 引数の -r で入力された文字列
233
+ #
234
+ #=== 戻り値
235
+ # Symbol:: 入力された文字列に対応した Symbol
236
+ #
237
+ #=== 例外
238
+ # なし
239
+ #
240
+ def set_render_opt(input_opt)
241
+ opt = ""
242
+ case input_opt.downcase
243
+ when 'nested'
244
+ opt = :nested
245
+ when 'expanded'
246
+ opt = :expanded
247
+ when 'compact'
248
+ opt = :compact
249
+ when 'compressed'
250
+ opt = :compressed
251
+ else
252
+ opt = :nested
253
+ end
254
+
255
+ return opt
256
+ end
257
+
258
+ #== saag#set_default_conf
259
+ # sass 実行時のオプション設定用メソッド。
260
+ # 省略時の入力パスと出力パスを設定する。
261
+ # ディレクトリの場合は '/' を付加する。
262
+ #
263
+ #=== 引数
264
+ # なし
265
+ #
266
+ #=== 戻り値
267
+ # なし
268
+ #
269
+ #=== 例外
270
+ # なし
271
+ #
272
+ def set_default_conf()
273
+ unless @conf[:in_path]
274
+ @conf[:in_path] = Dir.pwd + '/'
275
+ end
276
+ unless @conf[:out_path]
277
+ if File.directory?(@conf[:in_path])
278
+ @conf[:out_path] = @conf[:in_path]
279
+ else
280
+ @conf[:out_path] =File.dirname( @conf[:in_path]) + '/'
281
+ end
282
+ end
283
+ unless @conf[:render_opt]
284
+ @conf[:render_opt] = :nested
285
+ end
286
+
287
+ # プロセス開始時刻
288
+ @conf[:start_time] = Time.now
289
+ end
290
+
291
+ #== saag#get_file_list
292
+ # ファイルのパスと時刻のリストを作成する
293
+ #
294
+ #=== 引数
295
+ # _in_path_:: 引数の -i で入力されたパス
296
+ #
297
+ #=== 戻り値
298
+ # Array:: 時刻とパスを保持したファイルリスト。
299
+ #
300
+ #=== 例外
301
+ # なし
302
+ #
303
+ def get_file_list(in_path)
304
+ list = []
305
+ if File.file?(in_path)
306
+ list << create_file_data(in_path)
307
+ else
308
+ list = Dir.glob("#{in_path}**/*#{SASS_EXT}").map do |m|
309
+ if File.file?(m)
310
+ create_file_data(m)
311
+ end
312
+ end
313
+ end
314
+
315
+ return list.compact
316
+ end
317
+
318
+ #== saag#create_file_data
319
+ # ファイルパスと時刻等を保持した HASH を生成する。
320
+ # 生成する HASH の内容は以下の通り
321
+ # * :path => ファイルの絶対パス
322
+ # * :sub_path => 入力時に指定されたディレクトリ以降からのファイルパス
323
+ # * :time => 対象ファイルの mtime
324
+ # * :change => 前回との変更があったか?を現すフラグ(この時点では全て true)
325
+ #
326
+ #=== 引数
327
+ # _path_:: ファイルパス
328
+ #
329
+ #=== 戻り値
330
+ # Hash:: 時刻とパス等を保持した Hash データ。
331
+ #
332
+ #=== 例外
333
+ # なし
334
+ #
335
+ def create_file_data(path)
336
+ # 入力パスと実ファイルのパスの差分を出す(この差分が出力ディレクトリの連結パスとなる)
337
+ sub_path = ""
338
+ @logger.debug("create_file_data@path => [#{path}]")
339
+ @logger.debug("create_file_data@in_path => [#{@conf[:in_path]}]")
340
+ if path =~ /#{@conf[:in_path]}(.+)/
341
+ sub_path = $1
342
+ end
343
+
344
+ change = true
345
+ file_time = File.mtime(path)
346
+ # exclude オプションが無い(デフォルト時)はプロセスのスタート時刻よりも古い時刻のファイルは false にする
347
+ unless @conf[:exclude]
348
+ @logger.debug("no exclude option.")
349
+ change = false if(file_time < @conf[:start_time])
350
+ end
351
+
352
+ data = {
353
+ :path => path,
354
+ :sub_path => sub_path,
355
+ :time => file_time,
356
+ :change => change
357
+ }
358
+ @logger.debug("create_file_data@sub_path => #{sub_path}")
359
+ return data
360
+ end
361
+
362
+ #== saag#check_file_list
363
+ # 前回走査したファイルリストと、今回走査したファイルリストにおいて、
364
+ # ファイルの更新や追加があったリストの :change を true にし、そうでなければ false にする。
365
+ #
366
+ #=== 引数
367
+ # _old_list_:: 前回のループで取得したファイルリストの Array
368
+ # _new_list_:: 今回のループで取得したファイルリストの Array
369
+ #
370
+ #=== 戻り値
371
+ # Array:: change フラグの更新が終わった new_list
372
+ #
373
+ #=== 例外
374
+ # なし
375
+ #
376
+ def check_file_list(old_list, new_list)
377
+ return new_list if old_list.empty?
378
+
379
+ new_list.each do |new|
380
+ old_list.each do |old|
381
+ if(new[:path] == old[:path]) # 前回と今回で同じファイルがあれば、時刻の比較を行う
382
+ if(new[:time] > old[:time]) # 時刻が更新されていれば true
383
+ new[:change] = true
384
+ else
385
+ new[:change] = false
386
+ end
387
+ end
388
+ end
389
+ end
390
+
391
+ return new_list
392
+ end
393
+
394
+ #== saag#write_css_file
395
+ # 変換された CSS ファイルを出力する
396
+ #
397
+ #=== 引数
398
+ # _sass_file:: 変換対象のファイルの Hash
399
+ # _css_text_:: 変換された CSS テキストの String
400
+ #
401
+ #=== 戻り値
402
+ # なし
403
+ #
404
+ #=== 例外
405
+ # SystemCallError:: ファイル出力時の例外時に出力
406
+ #
407
+ def write_css_file(sass_file, css_text)
408
+ @logger.debug("@conf[:out_path] = #{@conf[:out_path]}, sass_file[:sub_path] = #{sass_file[:sub_path]}")
409
+ out_dir = ""
410
+ if sass_file[:sub_path].empty?
411
+ out_dir = @conf[:out_path]
412
+ else
413
+ out_dir = File.dirname(@conf[:out_path] + sass_file[:sub_path]) + '/'
414
+ end
415
+ filename = out_dir + File.basename(sass_file[:path], SASS_EXT) + CSS_EXT
416
+ @logger.debug("out_dir => #{out_dir}")
417
+
418
+ # ディレクトリが無ければ作成する
419
+ unless File.directory?(out_dir)
420
+ FileUtils.mkdir_p(out_dir)
421
+ @logger.info("create output directory => #{out_dir}")
422
+ end
423
+
424
+ File.open(filename, 'w') do |f|
425
+ f.puts(css_text)
426
+ end
427
+ @logger.info("generate css file => #{filename}")
428
+ end
429
+
430
+ #== saag#set_default_time
431
+ # 指定された path の :time 値をあり得ない値にして更新する。
432
+ # 更新に失敗したデータは時刻を現在より過去のものにして、次回のループで変換対象となるようにする。
433
+ #
434
+ #=== 引数
435
+ # _list_:: 走査対象のファイルリスト
436
+ # _path_:: 更新に失敗したファイルのパス
437
+ #
438
+ #=== 戻り値
439
+ # Array:: :time の更新が終わった new_list
440
+ #
441
+ #=== 例外
442
+ # なし
443
+ #
444
+ def set_default_time(file_list, path)
445
+ file_list.map do |list|
446
+ if list[:path] == path
447
+ list[:time] = Time.at(0) # 1970/1/1
448
+ end
449
+ list
450
+ end
451
+ end
452
+ end
453
+
data/spec/saag_spec.rb ADDED
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+
4
+ class Saag
5
+ # private methods to public
6
+ public :main_loop, :set_dir_path, :set_signal, :set_render_opt, :set_default_conf, :get_file_list, :create_file_data, :write_css_file, :check_file_list, :set_default_time
7
+ end
8
+
9
+ describe Saag, "test" do
10
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'saag'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
10
+
11
+
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: saag
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 5
9
+ version: 0.3.5
10
+ platform: ruby
11
+ authors:
12
+ - sugamasao
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2009-08-09 00:00:00 +09:00
18
+ default_executable:
19
+ - saag
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: haml
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: SAss Automatic monitor and Generate css file.
34
+ email: sugamasao@gmail.com
35
+ executables:
36
+ - saag
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - ChangeLog
41
+ - README.rdoc
42
+ - TODO
43
+ files:
44
+ - ChangeLog
45
+ - README.rdoc
46
+ - VERSION
47
+ - bin/saag
48
+ - lib/saag.rb
49
+ - TODO
50
+ has_rdoc: true
51
+ homepage: http://github.com/sugamasao/saag
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --title
57
+ - saag documentation
58
+ - --charset
59
+ - utf-8
60
+ - --opname
61
+ - index.html
62
+ - --line-numbers
63
+ - --main
64
+ - README.rdoc
65
+ - --inline-source
66
+ - --exclude
67
+ - ^(examples|extras)/
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 1
76
+ - 8
77
+ - 6
78
+ version: 1.8.6
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.6
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: SAss Automatic monitor and Generate css file.
93
+ test_files:
94
+ - spec/saag_spec.rb
95
+ - spec/spec_helper.rb