fdlint 0.1.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 (218) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +14 -0
  3. data/README.md +68 -0
  4. data/Rakefile +92 -0
  5. data/bin/fdlint +17 -0
  6. data/lib/base_parser.rb +143 -0
  7. data/lib/cmd_runner.rb +145 -0
  8. data/lib/context.rb +31 -0
  9. data/lib/css/parser.rb +186 -0
  10. data/lib/css/reader.rb +30 -0
  11. data/lib/css/rule/check_compression_rule.rb +48 -0
  12. data/lib/css/rule/checklist.rb +45 -0
  13. data/lib/css/struct.rb +111 -0
  14. data/lib/encoding_error.rb +6 -0
  15. data/lib/file_validator.rb +38 -0
  16. data/lib/helper/code_type.rb +50 -0
  17. data/lib/helper/color_string.rb +44 -0
  18. data/lib/helper/file_reader.rb +22 -0
  19. data/lib/helper/strenc.rb +65 -0
  20. data/lib/html/parser.rb +212 -0
  21. data/lib/html/query.rb +96 -0
  22. data/lib/html/rule/check_tag_rule.rb +80 -0
  23. data/lib/html/struct.rb +291 -0
  24. data/lib/js/expr/expr.rb +66 -0
  25. data/lib/js/expr/left_hand.rb +63 -0
  26. data/lib/js/expr/operate.rb +92 -0
  27. data/lib/js/expr/primary.rb +166 -0
  28. data/lib/js/parser.rb +116 -0
  29. data/lib/js/rule/all.rb +35 -0
  30. data/lib/js/rule/checklist.rb +41 -0
  31. data/lib/js/rule/file_checker.rb +42 -0
  32. data/lib/js/rule/helper.rb +96 -0
  33. data/lib/js/rule/no_global.rb +87 -0
  34. data/lib/js/stat/if.rb +25 -0
  35. data/lib/js/stat/iter.rb +85 -0
  36. data/lib/js/stat/stat.rb +117 -0
  37. data/lib/js/stat/switch.rb +65 -0
  38. data/lib/js/stat/try.rb +28 -0
  39. data/lib/js/stat/var.rb +40 -0
  40. data/lib/js/struct.rb +248 -0
  41. data/lib/log_entry.rb +49 -0
  42. data/lib/node.rb +28 -0
  43. data/lib/parse_error.rb +13 -0
  44. data/lib/parser_visitable.rb +138 -0
  45. data/lib/position_info.rb +46 -0
  46. data/lib/printer/base_printer.rb +24 -0
  47. data/lib/printer/console_printer.rb +66 -0
  48. data/lib/printer/nocolor_printer.rb +27 -0
  49. data/lib/printer/vim_printer.rb +19 -0
  50. data/lib/rule.rb +241 -0
  51. data/lib/rule_helper.rb +14 -0
  52. data/lib/runner.rb +225 -0
  53. data/rules.d/css.rule +127 -0
  54. data/rules.d/html.dtd.rule +22 -0
  55. data/rules.d/html.prop.rule +51 -0
  56. data/rules.d/html.tag.rule +136 -0
  57. data/rules.d/js.file.rule +13 -0
  58. data/rules.d/js.jquery.rule +56 -0
  59. data/rules.d/js.mergefile.rule +71 -0
  60. data/rules.d/js.rule +84 -0
  61. data/test/all_tests.rb +84 -0
  62. data/test/cli/cli_test.rb +70 -0
  63. data/test/cli/log_level_test.rb +51 -0
  64. data/test/cli/output_format_test.rb +47 -0
  65. data/test/cli/type_test.rb +77 -0
  66. data/test/css/mac_line_end_support_test.rb +38 -0
  67. data/test/css/parser_test.rb +276 -0
  68. data/test/css/rule/check_encoding_test.rb +66 -0
  69. data/test/css/rule/check_list_rule_test.rb +167 -0
  70. data/test/css/rule/compression_test.rb +53 -0
  71. data/test/css/rule/file_name_test.rb +76 -0
  72. data/test/fixtures/css/broken.css +4 -0
  73. data/test/fixtures/css/cbu/36.css +52 -0
  74. data/test/fixtures/css/cbu/china_top.css +324 -0
  75. data/test/fixtures/css/cbu/default-merge.css +3 -0
  76. data/test/fixtures/css/cbu/default.css +13 -0
  77. data/test/fixtures/css/cbu/diy-merge.css +25 -0
  78. data/test/fixtures/css/cbu/fns-v1.css +27 -0
  79. data/test/fixtures/css/cbu/index_v0.1.css +12 -0
  80. data/test/fixtures/css/cbu/merge.css +11 -0
  81. data/test/fixtures/css/cbu/min.css +2 -0
  82. data/test/fixtures/css/cbu/my_home_admin.css +126 -0
  83. data/test/fixtures/css/cbu/nav.css +95 -0
  84. data/test/fixtures/css/cbu/pic_list.css +386 -0
  85. data/test/fixtures/css/cbu/quote-edit.css +18 -0
  86. data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
  87. data/test/fixtures/css/cbu/v1.css +9 -0
  88. data/test/fixtures/css/css3.css +30 -0
  89. data/test/fixtures/css/empty-min.css +0 -0
  90. data/test/fixtures/css/empty.css +0 -0
  91. data/test/fixtures/css/font-family.css +4 -0
  92. data/test/fixtures/css/gb-good.css +14 -0
  93. data/test/fixtures/css/gb_using_star.css +4 -0
  94. data/test/fixtures/css/import.css +18 -0
  95. data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
  96. data/test/fixtures/css/mac-line-sep-err.css +1 -0
  97. data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
  98. data/test/fixtures/css/mac-line-sep-good.css +1 -0
  99. data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
  100. data/test/fixtures/css/simple.css +1 -0
  101. data/test/fixtures/css/using_expr.css +8 -0
  102. data/test/fixtures/css/using_hack.css +21 -0
  103. data/test/fixtures/css/using_id.css +1 -0
  104. data/test/fixtures/css/using_star.css +4 -0
  105. data/test/fixtures/css/utf8_good.css +6 -0
  106. data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
  107. data/test/fixtures/css/utf8_using_star.css +5 -0
  108. data/test/fixtures/html/1-1.html +120 -0
  109. data/test/fixtures/html/1-2.html +120 -0
  110. data/test/fixtures/html/cms.html +373 -0
  111. data/test/fixtures/html/css_out_of_head.html +9 -0
  112. data/test/fixtures/html/fdev-template.html +22 -0
  113. data/test/fixtures/html/google.com.html +33 -0
  114. data/test/fixtures/html/mixed_log_levels.html +4 -0
  115. data/test/fixtures/html/mixed_types.html +13 -0
  116. data/test/fixtures/html/no_dtd.html +6 -0
  117. data/test/fixtures/html/readme.html +94 -0
  118. data/test/fixtures/html/review.board.html +163 -0
  119. data/test/fixtures/html/syntax_err.html +3 -0
  120. data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
  121. data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
  122. data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
  123. data/test/fixtures/html/train/index.html +37 -0
  124. data/test/fixtures/html/train/test.html +1 -0
  125. data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
  126. data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
  127. data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
  128. data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
  129. data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
  130. data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
  131. data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
  132. data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
  133. data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
  134. data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
  135. data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
  136. data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
  137. data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
  138. data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
  139. data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
  140. data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
  141. data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
  142. data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
  143. data/test/fixtures/html/unescaped.html +2 -0
  144. data/test/fixtures/html/view.vm +916 -0
  145. data/test/fixtures/js/jquery-1.7.js +9300 -0
  146. data/test/fixtures/js/scope-test.js +22 -0
  147. data/test/helper.rb +41 -0
  148. data/test/html/mixed_type_test.rb +35 -0
  149. data/test/html/parser/parse_comment_test.rb +47 -0
  150. data/test/html/parser/parse_dtd_test.rb +46 -0
  151. data/test/html/parser/parse_script_tag_test.rb +55 -0
  152. data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
  153. data/test/html/parser/parse_with_diff_case_test.rb +38 -0
  154. data/test/html/parser/parse_with_emtpy_test.rb +22 -0
  155. data/test/html/parser/parse_with_multi_children_test.rb +27 -0
  156. data/test/html/parser/parse_with_multi_line_test.rb +41 -0
  157. data/test/html/parser/parse_with_prop_test.rb +88 -0
  158. data/test/html/parser/parse_with_script_tag_test.rb +26 -0
  159. data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
  160. data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
  161. data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
  162. data/test/html/parser/parse_with_style_tag_test.rb +22 -0
  163. data/test/html/parser/parse_with_text_test.rb +45 -0
  164. data/test/html/parser_test.rb +52 -0
  165. data/test/html/query_test.rb +52 -0
  166. data/test/html/rule/check_block_level_element_test.rb +52 -0
  167. data/test/html/rule/check_button_test.rb +45 -0
  168. data/test/html/rule/check_class_count_test.rb +36 -0
  169. data/test/html/rule/check_css_in_head_test.rb +53 -0
  170. data/test/html/rule/check_dtd_test.rb +46 -0
  171. data/test/html/rule/check_form_element_name_test.rb +49 -0
  172. data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
  173. data/test/html/rule/check_html_template_test.rb +103 -0
  174. data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
  175. data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
  176. data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
  177. data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
  178. data/test/html/rule/check_no_import_css_test.rb +36 -0
  179. data/test/html/rule/check_prop_have_value_test.rb +32 -0
  180. data/test/html/rule/check_prop_seperator_test.rb +32 -0
  181. data/test/html/rule/check_style_prop_test.rb +30 -0
  182. data/test/html/rule/check_tag_closed_test.rb +59 -0
  183. data/test/html/rule/check_tag_downcase_test.rb +51 -0
  184. data/test/html/rule/check_unescape_char_test.rb +35 -0
  185. data/test/html/rule/check_unique_import_test.rb +56 -0
  186. data/test/html/rule_test.rb +62 -0
  187. data/test/js/expr/expr.rb +57 -0
  188. data/test/js/expr/left_hand.rb +25 -0
  189. data/test/js/expr/operate.rb +145 -0
  190. data/test/js/expr/primary.rb +89 -0
  191. data/test/js/parser_test.rb +98 -0
  192. data/test/js/rule/alert_check_test.rb +37 -0
  193. data/test/js/rule/all_test.rb +23 -0
  194. data/test/js/rule/base_test.rb +34 -0
  195. data/test/js/rule/file_checker_test.rb +131 -0
  196. data/test/js/rule/jq_check_test.rb +90 -0
  197. data/test/js/rule/nest_try_catch_test.rb +71 -0
  198. data/test/js/rule/new_object_and_new_array_test.rb +38 -0
  199. data/test/js/rule/no_eval_test.rb +34 -0
  200. data/test/js/rule/no_global_test.rb +88 -0
  201. data/test/js/rule/private_method_check_test.rb +58 -0
  202. data/test/js/rule/semicolon_test.rb +63 -0
  203. data/test/js/rule/stat_if_with_brace_test.rb +68 -0
  204. data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
  205. data/test/js/rule/use_strict_equal_test.rb +44 -0
  206. data/test/js/rule_test.rb +47 -0
  207. data/test/js/stat/if.rb +26 -0
  208. data/test/js/stat/iter.rb +115 -0
  209. data/test/js/stat/stat.rb +91 -0
  210. data/test/js/stat/switch.rb +37 -0
  211. data/test/js/stat/try.rb +32 -0
  212. data/test/js/stat/var.rb +38 -0
  213. data/test/parser_visitable_test.rb +102 -0
  214. data/test/position_info_test.rb +66 -0
  215. data/test/rule_dsl/dsl_basic_test.rb +91 -0
  216. data/test/rule_dsl/importing_test.rb +48 -0
  217. data/test/runner/log_level_test.rb +58 -0
  218. metadata +317 -0
@@ -0,0 +1,138 @@
1
+ require 'observer'
2
+
3
+ require_relative 'log_entry'
4
+
5
+ module XRay
6
+
7
+ class VisitResult < LogEntry
8
+
9
+ attr_reader :node
10
+
11
+ def initialize(node, message, level)
12
+ @node = node
13
+ pos = node.position || Position.new(0, 0, 0)
14
+ super message, level, pos.row, pos.column
15
+ end
16
+ end
17
+
18
+
19
+ module ParserVisitable
20
+
21
+ include Observable
22
+
23
+ def self.included(klass)
24
+ klass.public_instance_methods(true).each do |method|
25
+ wrap(klass, method)
26
+ end
27
+
28
+ def klass.method_added(method)
29
+ unless @flag
30
+ @flag = true
31
+ ParserVisitable.wrap(self, method)
32
+ @flag = false
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.wrap(klass, method)
38
+ method = method.to_s
39
+ unless method.index 'parse_'
40
+ return
41
+ end
42
+
43
+ klass.instance_eval do
44
+ old_method = "#{method}_without_visit"
45
+ name = method.sub /^parse_/, ''
46
+ alias_method old_method, method
47
+
48
+ define_method(method) do |*args, &block|
49
+ before name, *args
50
+ node = self.send(old_method, *args, &block)
51
+ node && visit(name, node)
52
+ node
53
+ end
54
+ end
55
+ end
56
+
57
+ def parse_no_throw
58
+ root = []
59
+ begin
60
+ root = self.parse
61
+ rescue ParseError => e
62
+ @results ||= []
63
+ @results << LogEntry.new(e.to_s, :fatal, e.position.row, e.position.column)
64
+ end
65
+ root
66
+ end
67
+
68
+ def add_visitor(visitor)
69
+ @visitors ||= {}
70
+
71
+ visitor.class.public_instance_methods(true).each do |method|
72
+ method = method.to_s
73
+ if method.index('visit_') == 0 ||
74
+ method.index('before_parse_') == 0
75
+ cache = @visitors[method] ||= []
76
+ cache << visitor
77
+ end
78
+ end
79
+ end
80
+
81
+ def add_visitors(visitors)
82
+ visitors.each { |visitor| add_visitor visitor }
83
+ end
84
+
85
+ def results
86
+ @results || []
87
+ end
88
+
89
+ private
90
+
91
+ def visit(name, node)
92
+ walk(name, 'visit_', node) do |result|
93
+ result && notify(node, result)
94
+ end
95
+ end
96
+
97
+ def before(name, *args)
98
+ walk name, 'before_parse_', *args
99
+ end
100
+
101
+ def walk(name, prefix, *args, &block)
102
+ unless @visitors
103
+ return
104
+ end
105
+
106
+ method = prefix + name
107
+ visitors = @visitors[method]
108
+ visitors && visitors.each do |visitor|
109
+ result = visitor.send method, *args
110
+ block && block.call(result)
111
+ end
112
+ end
113
+
114
+ def notify(node, results)
115
+ @results ||= []
116
+ if results.is_a?(Array) && results.size > 0
117
+ if results[0].is_a? Array
118
+ results.each do |ret|
119
+ do_notify node, ret, ret.is_a?(VisitResult)
120
+ end
121
+ else
122
+ do_notify node, results
123
+ end
124
+ elsif results.is_a? VisitResult
125
+ do_notify node, results, true
126
+ end
127
+ end
128
+
129
+ def do_notify(node, ret, flag = false)
130
+ ret = VisitResult.new(node, ret[0], ret[1]) unless flag
131
+ @results << ret
132
+ self.changed
133
+ notify_observers ret, self
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,46 @@
1
+ module XRay
2
+
3
+ Position = Struct.new(:pos, :row, :column)
4
+ class Position
5
+ def to_s
6
+ "[#{row},#{column}]"
7
+ end
8
+ end
9
+
10
+ class PositionInfo
11
+
12
+ def initialize(text)
13
+ lines = text.split(/\n/)
14
+
15
+ @lines = []
16
+ num = 0
17
+ lines.each do |line|
18
+ num += line.length + 1
19
+ @lines << num
20
+ end
21
+
22
+ @len = lines.length
23
+ end
24
+
25
+ def locate(pos)
26
+ if @row && pos >= @lines[@row -1] && pos < @lines[@row]
27
+ row = @row
28
+ else
29
+ row = @row = find(0, @len - 1, pos)
30
+ end
31
+ col = row > 0 ? pos - @lines[row - 1] : pos
32
+ Position.new(pos, row + 1, col + 1)
33
+ end
34
+
35
+ private
36
+
37
+ def find(low, high, pos)
38
+ return low unless low < high
39
+
40
+ mid = (low + high) / 2
41
+ pos < @lines[mid] ? find(low, mid, pos) :
42
+ find(mid + 1, high, pos)
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ module XRay
2
+
3
+ def self.printer
4
+ @@printer
5
+ end
6
+
7
+ def self.register_printer(klass)
8
+ @@printer = klass
9
+ end
10
+
11
+ class BasePrinter
12
+
13
+ XRay.register_printer self
14
+
15
+ def initialize(results, opt)
16
+ @results, @opt = results, opt
17
+ end
18
+
19
+ def print
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'base_printer'
2
+
3
+ if ENV['OS'] =~ /windows/i
4
+ require 'win32console'
5
+ end
6
+
7
+
8
+ module XRay
9
+
10
+ class ConsolePrinter < BasePrinter
11
+
12
+ XRay.register_printer self
13
+
14
+ def print
15
+ if @opt[:source]
16
+ print_with_source
17
+ else
18
+ if @results.empty?
19
+ puts "[OK]".white.green_bg << " #{@opt[:file]}"
20
+ else
21
+ prf = @opt[:prefix]
22
+ suf = @opt[:suffix]
23
+
24
+ puts ""
25
+ puts "[EE] #{@opt[:file]}".white.magenta_bg
26
+
27
+ @results.each do |r|
28
+ puts "#{prf}#{r.to_color_s}#{suf}"
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+
35
+ def print_with_source
36
+ if @results.empty?
37
+ puts "[OK]".white.green_bg << " #{@opt[:file]}"
38
+ else
39
+ source = @opt[:source]
40
+ lines = source.split(/\r\n|\n|\r/)
41
+ prf = @opt[:prefix]
42
+ suf = @opt[:suffix]
43
+
44
+ puts "[EE] #{@opt[:file]}".white.magenta_bg
45
+
46
+ @results.each do |r|
47
+ if r.row && r.row > 0
48
+ col = r.column - 1
49
+ row = r.row - 1
50
+ left = col - 50
51
+ right = col + 50
52
+ left = 0 if left < 0
53
+
54
+ puts "#{prf}#{lines[row][left..right].gsub(/\t/, ' ')}"
55
+ puts "#{prf}#{' ' * (col - left)}^ #{r.to_color_s}"
56
+ puts "\n"
57
+ else
58
+ puts "#{r.to_color_s}#{suf}\n"
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'base_printer'
2
+
3
+ module XRay
4
+
5
+ class NoColorPrinter < BasePrinter
6
+
7
+ XRay.register_printer self
8
+
9
+ def print
10
+ if @results.empty?
11
+ puts "[OK] #{@opt[:file]}"
12
+ else
13
+ prf = @opt[:prefix]
14
+ suf = @opt[:suffix]
15
+ out = @opt[:out]
16
+
17
+ puts "[EE] #{@opt[:file]}"
18
+
19
+ @results.each do |r|
20
+ puts "#{prf}#{r}#{suf}"
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'base_printer'
2
+
3
+ module XRay
4
+
5
+ class VimPrinter < BasePrinter
6
+
7
+ XRay.register_printer self
8
+
9
+ def print
10
+ prf = (@opt[:file]||'-').dup.utf8!
11
+ @results.each do |r|
12
+ puts "#{prf}:[#{r.level}]:#{r.row},#{r.column}:#{r.message}"
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
data/lib/rule.rb ADDED
@@ -0,0 +1,241 @@
1
+ require 'find'
2
+ require_relative 'js/parser'
3
+ require_relative 'html/parser'
4
+ require_relative 'css/parser'
5
+ require_relative 'helper/file_reader'
6
+ require_relative 'node'
7
+
8
+ module XRay
9
+
10
+ module Rule
11
+
12
+ STYLE_TYPES = [:css, :js, :html]
13
+
14
+ def self.methods_to_keywords(klass)
15
+ klass.instance_methods.grep( /^parse_/ ).map {|m| m[/^parse_(.*)/,1]}
16
+ end
17
+
18
+ RULE_PATH = File.expand_path '../rules.d', File.dirname(__FILE__)
19
+ KEYWORDS = %w(file merge_importing merge_file)
20
+
21
+ KEYWORDS.concat methods_to_keywords(XRay::JS::Parser)
22
+ KEYWORDS.concat methods_to_keywords(XRay::CSS::Parser)
23
+ KEYWORDS.concat methods_to_keywords(XRay::HTML::Parser)
24
+
25
+ @@common_rules = []
26
+ @@context = nil
27
+
28
+ def syntax(*type)
29
+ if type.empty?
30
+ @syntax || 'common'
31
+ else
32
+ @syntax = type.first
33
+ end
34
+ end
35
+
36
+ def common(&block)
37
+ syntax nil
38
+ yield if block_given?
39
+ end
40
+
41
+ KEYWORDS.each do |kw|
42
+ eval <<-RUBY
43
+ def #{kw}_rules
44
+ @@common_rules.select {|r| r[:name].to_s.include? "#{kw}" }
45
+ end
46
+
47
+ def check_#{kw}( *target, &block )
48
+ if block_given?
49
+ self.send :"add_\#{syntax}_rule", :check_#{kw}, &block
50
+ elsif target.size > 0
51
+ do_check #{kw}_rules, *target
52
+ end
53
+ end
54
+
55
+ RUBY
56
+
57
+ STYLE_TYPES.each do |syn|
58
+ eval <<-RUBY
59
+ def #{syn}_#{kw}_rules
60
+ #{syn}_rules.select {|r| r[:name].to_s.include? "#{kw}" }
61
+ end
62
+
63
+ def only_#{syn}_#{kw}_rules
64
+ only_#{syn}_rules.select {|r| r[:name].to_s.include? "#{kw}" }
65
+ end
66
+
67
+ def check_#{syn}_#{kw}( *target, &block )
68
+ if block_given?
69
+ self.send :"add_#{syn}_rule", :check_#{syn}_#{kw}, &block
70
+ elsif target.size > 0
71
+ do_check #{syn}_#{kw}_rules, *target
72
+ end
73
+ end
74
+
75
+ RUBY
76
+ end
77
+ end
78
+
79
+ STYLE_TYPES.each do |syn|
80
+
81
+ eval <<-RUBY
82
+
83
+ @@#{syn}_rules = []
84
+
85
+ def #{syn}
86
+ syntax #{syn.inspect}
87
+ yield if block_given?
88
+ end
89
+
90
+ def add_#{syn}_rule( name, &block )
91
+ @@#{syn}_rules << { :name => name, :block => block }
92
+ end
93
+
94
+ def #{syn}_rules
95
+ @@common_rules + @@#{syn}_rules
96
+ end
97
+
98
+ def only_#{syn}_rules
99
+ @@#{syn}_rules
100
+ end
101
+
102
+ def clear_#{syn}_rules
103
+ @@#{syn}_rules.clear
104
+ end
105
+ RUBY
106
+ end
107
+
108
+ def clear_all_rules
109
+ @@imported.clear
110
+ @@common_rules.clear
111
+ STYLE_TYPES.each { |syn| send(:"clear_#{syn}_rules") }
112
+ end
113
+
114
+ def do_check( rules, *args )
115
+ @@context = self
116
+ target = args.first
117
+ rules.inject([]) do |results, r|
118
+ result = r[:block].call(*args)
119
+ results << result if result
120
+ results
121
+ end
122
+ end
123
+
124
+ def context
125
+ @@context
126
+ end
127
+
128
+ def method_missing( name , *args, &block )
129
+ if cmd = cmd_name(name) and self.respond_to? cmd
130
+ send(cmd, name, &block )
131
+ def_rule_cmd(cmd, name, &block) if block_given?
132
+ else
133
+ super
134
+ end
135
+ end
136
+
137
+ def def_rule_cmd(cmd, name, &block)
138
+ XRay::Rule.instance_eval do
139
+ define_method(name) do |*tar, &b|
140
+ if tar.size > 0
141
+ @@context = self
142
+ block.call *tar
143
+ else
144
+ send cmd, name, &b
145
+ end
146
+ end
147
+ end
148
+ end
149
+ private :def_rule_cmd
150
+
151
+ def cmd_name(name)
152
+ name = name.to_s
153
+ KEYWORDS.each do |kw|
154
+ if name.start_with? "check_#{kw}"
155
+ return :"check_#{kw}"
156
+ else
157
+ STYLE_TYPES.each do |s|
158
+ cmd = "check_#{s}_#{kw}"
159
+ return cmd if name.start_with? cmd
160
+ end
161
+ end
162
+ end
163
+ nil
164
+ end
165
+
166
+ def add_common_rule( name, &block )
167
+ @@common_rules << { :name => name , :block => block }
168
+ end
169
+
170
+ alias :add_rule :add_common_rule
171
+
172
+
173
+
174
+ @@imported = []
175
+
176
+ def import( name )
177
+ if name.is_a? Symbol
178
+ path = File.join(RULE_PATH, "#{name}.rule")
179
+ else
180
+ path = name
181
+ end
182
+ path = File.expand_path path
183
+ unless imported? path
184
+ @@imported << path
185
+ src, enc = ::XRay::Helper::FileReader.readfile path
186
+ instance_eval src
187
+ end
188
+ end
189
+
190
+ def import_all
191
+ Find.find( RULE_PATH ) do |rule|
192
+ import rule if rule.end_with? '.rule'
193
+ end
194
+ end
195
+
196
+ def self.import_all
197
+ "".extend(self).import_all
198
+ end
199
+
200
+ def imported?(path)
201
+ @@imported.include? path
202
+ end
203
+
204
+ def imported
205
+ @@imported
206
+ end
207
+
208
+ end
209
+
210
+ end
211
+
212
+
213
+ if __FILE__ == $0
214
+
215
+ extend XRay::Rule
216
+
217
+ ## Rule DSL example
218
+ css {
219
+ check_file_without_min { |file|
220
+ ['should not be min file', :error] if file =~ /-min\.css$/
221
+ }
222
+
223
+ }
224
+
225
+ common {
226
+ check_file { |file|
227
+ ['should use "-" instead of "_"', :warn] if file =~ /_/
228
+ }
229
+ }
230
+
231
+ ## Test
232
+ import_all
233
+
234
+ puts file_rules
235
+ puts css_file_rules
236
+
237
+ puts check_file_without_min('test-min.css').inspect
238
+ puts check_css_file('test_a-min.css').inspect
239
+ puts check_css_value('expression(test)\0').inspect
240
+
241
+ end
@@ -0,0 +1,14 @@
1
+ module XRay
2
+ module RuleHelper
3
+
4
+ def dispatch(items, node)
5
+ results = []
6
+ items.each do |item|
7
+ result = self.send(item, node)
8
+ result && (results << result.flatten)
9
+ end
10
+ results
11
+ end
12
+
13
+ end
14
+ end