outlaw 0.1.2 → 0.1.3

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.
data/README.md CHANGED
@@ -1,35 +1,108 @@
1
1
  #Outlaw
2
2
 
3
3
  ##Keep bad code out of your projects. Your idea of bad code, no one elses.
4
+ ##Because good documentation should be executable.
5
+
6
+ ###NOTE: Outlaw can evaluate any version ruby code, *BUT* it runs on only 1.9 -- set your system ruby to 1.9 to use
4
7
 
5
8
  ### Part of MendicantUniversity.org S10 class, personal project.
6
- From the included outlawed.rb example file for custom rule definition:
7
9
 
8
- module Outlaw
9
- outlaw "@@", "Class variables are evil"
10
- outlaw "protected", "use private or public, protected is silly in ruby"
11
- outlaw "module :token end", "nest modules to avoid empty module declarations"
12
- outlaw "eval", "never eval, rarely class_eval or instance_eval, but never eval"
13
- end
10
+ The current version of outlaw takes a user provided configuration file
11
+ (currently named simply '.outlawed' in the project directory or user's home
12
+ directory) and parses a series of method calls to the outlaw method (defined
13
+ within the Outlaw module namespace, but module_eval'd so you don't need to
14
+ namespace the file). You can also define constants in your .outlawed file as
15
+ collections of strings for use in your rules, but that will be addressed below.
16
+
17
+ Each call to the outlaw method consists of two string arguments, the first an
18
+ anti-pattern you wish to prohibit usage of in one or more projects, and the
19
+ second an explanation to be provided when the anti-pattern is detected.
20
+
21
+ ### Syntax for rule creation:
22
+
23
+ Some examples are included in the .outlawed.example file for reference:
24
+
25
+ outlaw "@@", "Class variables are evil"
26
+
27
+ outlaw "protected", "use private or public, protected
28
+ is silly in ruby"
29
+
30
+ outlaw "eval", "never eval, rarely class_eval or
31
+ instance_eval, but never eval"
32
+
33
+ outlaw "module :token end", "nest modules to avoid empty module
34
+ declarations"
35
+
36
+ outlaw "class :symbol < :core_class", "core classes implemented in c,
37
+ can cause bad mojo"
38
+
39
+ outlaw :trailing_whitespace
40
+
41
+
42
+ The first three examples are actual ruby keywords and features being outlawed
43
+ and may not require much explanation except to indicate that they are detected
44
+ via regular expression matches constructed from the strings, and attempt to use
45
+ word boundaries intelligently so that eval is detected but not module_eval.
46
+
47
+ The bottom two examples above use ruby symbols as standin variable or
48
+ parameter names for identifiers that are matched at runtime with local
49
+ variables, instance variables, class names and constants that may appear
50
+ within the ruby program being analyzed. Here, :symbol can be any ruby symbol
51
+ if it appears only once, though if used multiple times it will only match the
52
+ the same identifier on subsequent usage. :core_class as used above is a
53
+ special case where Outlaw has internally defined a constant called CORE_CLASS
54
+ as a collection of string objects each containing the name of one of ruby's
55
+ core classes. You can define your own similar collections in the .outlawed
56
+ file (to be loaded from an external data file preferably, if more than a few
57
+ values) and then reference CONST_NAME as :const_name in your outlaw anti-
58
+ patterns as above. Presently mutliple references to the same collection
59
+ are independent, but if there is interest special handling could be added to
60
+ also match specific instances of a collection much like the symbol handling.
61
+
62
+ The last one is a custom rule that calls a method defined on the Outlaw
63
+ module called 'trailing_whitespace' which returns a Rule object. Any
64
+ such new methods may be defined in the .outlawed file (since it is
65
+ module_eval'd into the outlaw namespace) and then outawed the same way.
66
+
67
+
68
+ Outlaw currently ignores whitespace, parentheses and new lines, though I have
69
+ ideas to change this behavior dynamically in certain rules if desired.
70
+
71
+ Execute outlaw on your project from the root directory by simply entering
72
+ "outlaw" into your shell, or specify another directory to run
73
+ on with "outlaw /path/to/dir"
74
+
75
+ Before using outlaw in a project you should create a .outlawed file which
76
+ Outlaw will read rules from.
14
77
 
15
- ### Proposed syntax for DSL:
78
+ It comes with an example file (.outlawed.example) which is included in the
79
+ gem and will be loaded if no .outlawed file is found in current directory or
80
+ home directory, and will warn you to provide a real file (and provide
81
+ location of the sample file in your system from the gem installation).
16
82
 
17
- A defined collection exists for core classes, such that
83
+ ###Planned features (unimplemented):
84
+ *Customize sensitivty , for instance whitespace is currently ignored, but
85
+ could enforce style conventions with some whitespace sensitive rules.
86
+ Also ignores parens, which might be required or prohibited in some
87
+ context.
88
+ *Specify AST-nodes of interest, and within them allow arbitrary amounts of
89
+ code with a :disjoint_code_seperator token.
18
90
 
19
- outlaw "class :symbol < :core_class",
20
- "core classes implemented in c, can cause bad mojo"
91
+ This should allow, for instance, something like the following, which is not
92
+ currently possible to outlaw in a useful way:
21
93
 
22
- will outlaw subclassing from any core class
94
+ outlaw ":conditional_branch
95
+ unless
96
+ :disjoint_code_seperator
97
+ else",
98
+ "If you write unless else and think it makes sense then you are a
99
+ cylon"
23
100
 
101
+ *Integrate Rails Best Practices gem, Reek gem, and perhaps others, so that individual issue
102
+ detections they provide can be added as rules in the outlawed file while
103
+ ignoring/not running other detection routines.
24
104
 
25
- Users can extend new defined collections and by creating new constants
26
- defined as arrays of variable names to match in example code the same
27
- way :core_class is used above
105
+ *Automate optional integration with rake task and/or githooks for
106
+ enforcement/notification of rules in a project.
28
107
 
29
- Disjoin code segments can be provided as a single example (assuming they
30
- occur in the same file) by inserting a :disjoint_code_seperator token in
31
- the outlawed sample definition. I don't have a good use case for this
32
- at present though, and since it will be difficult to handle disjoint
33
- code across different files this may not be worthwhile... but similar
34
- special case symbol meanings may be useful, and suggestions or examples
35
- are welcome.
108
+ *Specify classes of rules, such as log, warn and prevent for differing behavior regarding violations at runtime.
data/bin/outlaw CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "outlaw"
3
+ require_relative "../lib/outlaw"
4
4
  if File.exists?(Dir.pwd + '/.outlawed')
5
- load Dir.pwd + "/.outlawed"
5
+ File.open(Dir.pwd + "/.outlawed") {|f| Outlaw.module_eval(f.read)}
6
6
  elsif File.exists?(Dir.home + '/.outlawed')
7
- load Dir.home + '/.outlawed'
7
+ File.open(Dir.home + '/.outlawed') {|f| Outlaw.module_eval(f.read)}
8
8
  else
9
9
  puts "
10
10
  ***NOTICE:***
@@ -14,7 +14,8 @@ else
14
14
  at pwd first for ./outlawed and then at ~/.outlawed and defaults to example
15
15
  if neither is found\n\n"
16
16
 
17
- load File.dirname(File.dirname(`gem which outlaw`.chop)) + '/examples/.outlawed.example'
17
+ File.open(File.dirname(File.dirname(`gem which outlaw`.chop)) +
18
+ '/examples/.outlawed.example') {|f| Outlaw.module_eval(f.read)}
18
19
  end
19
20
 
20
21
  puts Outlaw.enforce(ARGV[0] ? ARGV[0] : ".")
@@ -1,5 +1,6 @@
1
1
  outlaw "@@", "class variables are evil"
2
2
  outlaw "protected", "use private or public, protected is silly in ruby"
3
3
  outlaw "module :token end", "nest modules to avoid empty module declarations"
4
- outlaw "eval", "never eval, rarely class_eval or instance_eval, but never eval"
5
- outlaw "class :symbol < :core_class", "core classes implemented in c, can cause bad mojo"
4
+ outlaw "eval", "never eval, rarely class_eval or instance_eval, but never eval"
5
+ outlaw "class :symbol < :core_class", "core classes implemented in c, can cause bad mojo"
6
+ outlaw :trailing_whitespace
@@ -1,20 +1,34 @@
1
1
  require 'ripper'
2
- require_relative 'outlaw/law_dsl'
2
+ require_relative 'outlaw/law_parser'
3
3
  require_relative 'outlaw/enforcement'
4
4
  require_relative 'outlaw/rule'
5
+ require_relative 'outlaw/rule_methods'
6
+
5
7
 
6
- def outlaw(restriction, message)
7
- law = Outlaw::LawDSL.parse(restriction, message)
8
- Outlaw::Enforcement.add(law)
9
- end
10
8
 
11
9
  module Outlaw
12
- def self.enforce(dir=".")
10
+ extend self
11
+ attr_accessor :ignore_types, :param_types
12
+
13
+ def outlaw(pattern, message=nil)
14
+ if pattern.kind_of?(String)
15
+ rule = Rule.new(pattern, message)
16
+ Outlaw::Enforcement.add(rule)
17
+ else
18
+ Outlaw::Enforcement.add(self.send(pattern))
19
+ end
20
+ end
21
+
22
+ def enforce(dir=".")
13
23
  Outlaw::Enforcement.process_directory(dir)
14
24
  end
15
- PARAM_TYPES = [:on_const, :on_ident, :on_ivar, :on_cvar]
16
- IGNORE_TYPES = [:on_sp, :on_nl, :on_ignored_nl, :on_rparen, :on_lparen]
17
- SPECIAL_CASES = [:disjoint_code_seperator] #need to work on naming here
18
- CORE_CLASSES_FILE = File.expand_path("../../data/core_classes.txt", __FILE__)
19
- CORE_CLASS = File.readlines(CORE_CLASSES_FILE).map &:chomp
25
+ #these come from ripper's Lexer
26
+ self.param_types = [:on_const, :on_ident, :on_ivar, :on_cvar]
27
+ self.ignore_types = [:on_sp, :on_nl, :on_ignored_nl, :on_rparen, :on_lparen]
28
+ WHITESPACE = [:on_sp, :on_nl, :on_ignored_nl]
29
+ VERTICAL_WHITESPACE = [:on_nl, :on_ignored_nl]
30
+ SPECIAL_CASES = [:whitespace_sensitive, :vertical_whitespace_sensitive]
31
+
32
+ CORE_CLASSES_FILE = File.expand_path("../../data/core_classes.txt", __FILE__)
33
+ CORE_CLASS = File.readlines(CORE_CLASSES_FILE).map &:chomp
20
34
  end
@@ -1,10 +1,10 @@
1
1
  module Outlaw
2
2
  class Enforcement
3
3
  class << self
4
- attr_reader :laws
5
- def add(law)
6
- @laws ||= []
7
- @laws << law
4
+ attr_reader :rules
5
+ def add(rule)
6
+ @rules ||= []
7
+ @rules << rule
8
8
  end
9
9
 
10
10
  def process_directory(path)
@@ -21,10 +21,10 @@ module Outlaw
21
21
  def handle(file)
22
22
  if file.match(/.rb$/)
23
23
  text = File.open(file) {|f| f.read}
24
- laws.each do |law|
25
- if law.call(text)
26
- puts "Outlaw Violation in file: #{file}\nRestriction:" +
27
- "#{law.restriction}\n\n#{law.message}"
24
+ rules.each do |rule|
25
+ if rule.violation?(text)
26
+ puts "Outlaw Violation in file: #{file}\nRestriction:\n" +
27
+ "#{rule.pattern}\n#{rule.message}\n\n"
28
28
  end
29
29
  end
30
30
  end
@@ -0,0 +1,73 @@
1
+ module Outlaw
2
+ module LawParser
3
+ extend self
4
+ def parse(restriction, rule)
5
+ tokens = restriction.split
6
+ parsed_restriction = []
7
+ tokens.each do |token|
8
+ case
9
+ when special_case?(string_to_sym(token))
10
+ handle_special(token, rule)
11
+ when multipart?(token) #this handles multi-token literals, Const.new etc
12
+ parsed_restriction += Ripper.lex(token)
13
+ .reduce([]){|array, tkn|
14
+ array << token_type_regex(tkn) }
15
+ when defined_collection?(token)
16
+ parsed_restriction << Outlaw.const_get(string_to_sym(token.upcase))
17
+ when parameter?(token)
18
+ parsed_restriction << string_to_sym(token)
19
+ else
20
+ parsed_restriction += build_regex(token)
21
+ end
22
+ end
23
+ build_block(parsed_restriction)
24
+ end
25
+
26
+ private
27
+
28
+ def handle_special(token, rule)
29
+ rule.modifications ||= []
30
+ rule.modifications << string_to_sym(token)
31
+ end
32
+
33
+ def special_case?(token)
34
+ SPECIAL_CASES.include?(token)
35
+ end
36
+
37
+ def token_type_regex(token)
38
+ /#{token[2]}/
39
+ end
40
+
41
+ def parameter?(token)
42
+ token[0].chr == ':'
43
+ end
44
+
45
+ def defined_collection?(token)
46
+ parameter?(token) && Outlaw.const_defined?(string_to_sym(token.upcase))
47
+ end
48
+
49
+ def string_to_sym(str)
50
+ str[1..-1].to_sym
51
+ end
52
+
53
+ def build_regex(token)
54
+ #fully expect this hack to come back & haunt me, but passes curr. examples
55
+ [/\A#{token}/]
56
+ end
57
+
58
+ def multipart?(token)
59
+ !parameter?(token) && Ripper.lex(token).count > 1
60
+ end
61
+
62
+ def build_block(pattern)
63
+ ->(file) do
64
+ program = Ripper.tokenize(file)
65
+ program.each_with_index do |token, index|
66
+ next unless token.match(pattern.first)
67
+ return true if Rule.test(program, index, pattern)
68
+ end
69
+ return false
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,18 +1,50 @@
1
1
  module Outlaw
2
2
  class Rule
3
- NoDetectionBlockProvided = Class.new(StandardError)
4
- attr_reader :message, :restriction
5
- def initialize(message, restriction, &detection_block)
6
- raise NoDetectionBlockProvided unless detection_block
7
- @message = message
8
- @restriction = restriction
3
+ attr_reader :message, :pattern, :detection_block
4
+ attr_accessor :modifications
5
+ def initialize(pattern, message=nil, &detection_block)
6
+ @pattern = pattern
7
+ @message = message ? message : "Don't do this: #{pattern}"
9
8
  @detection_block = detection_block
10
9
  end
11
10
 
12
- def call(code)
13
- @detection_block.call(code)
11
+ def violation?(code)
12
+ @detection_block = LawParser.parse(pattern, self) if detection_block.nil?
13
+ detect_violation(code)
14
14
  end
15
15
 
16
+ private
17
+
18
+ def detect_violation(code)
19
+ defaults = apply_modifications
20
+ result = detection_block.call(code)
21
+ apply_modifications(defaults)
22
+ result
23
+ end
24
+
25
+ def apply_modifications(restore=nil)
26
+ return nil if modifications.nil? && restore.nil?
27
+ default_ignores = Outlaw.ignore_types.clone
28
+ default_params = Outlaw.param_types.clone
29
+ if modifications.include?(:whitespace_sensitive)
30
+ WHITESPACE.each do |ws|
31
+ Outlaw.ignore_types.delete(ws)
32
+ Outlaw.param_types << ws
33
+ end
34
+ end
35
+ if modifications.include?(:vertical_whitespace_sensitive)
36
+ VERTICAL_WHITESPACE.each do |ws|
37
+ Outlaw.ignore_types.delete(ws)
38
+ Outlaw.param_types << ws
39
+ end
40
+ end
41
+ Outlaw.ignore_types = restore.first if restore
42
+ Outlaw.param_types = restore.last if restore
43
+ [default_ignores, default_params]
44
+ end
45
+
46
+ public
47
+
16
48
  class << self
17
49
  def test(program, start_index, pattern)
18
50
  pattern_index = 0
@@ -20,8 +52,8 @@ module Outlaw
20
52
  start_index.upto(program.length) do |index|
21
53
  code = program[index]
22
54
  part = pattern[pattern_index]
23
-
24
- next if IGNORE_TYPES.include? token_type(code)
55
+ return false if code.nil?
56
+ next if Outlaw.ignore_types.include?(token_type(code))
25
57
  return false unless match_token?(code, part, params[part])
26
58
  pattern_index +=1
27
59
  return true if pattern_index >= pattern.length
@@ -64,7 +96,7 @@ module Outlaw
64
96
 
65
97
  def param_type_equal(lex, param)
66
98
  #for now just check if it's a variable type, not kw, ws or other token
67
- PARAM_TYPES.include? lex
99
+ Outlaw.param_types.include? lex
68
100
  end
69
101
 
70
102
  def token_type(code)
@@ -0,0 +1,8 @@
1
+ module Outlaw
2
+ def trailing_whitespace
3
+ Rule.new("Trailing whitespace",
4
+ "Trailing whitespace is ugly and can mess up version history") do |file|
5
+ file.match(/ \n/)
6
+ end
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Outlaw
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -1,59 +1,61 @@
1
1
  require_relative 'test_helper'
2
2
 
3
3
  module Outlaw
4
- describe LawDSL do
4
+ describe LawParser do
5
5
  it "returns a Rule which is called on code and returns true or false" do
6
6
  end
7
7
 
8
8
  before do
9
- @okay_file = <<CODE
10
- class Whatever < Set
11
- def okaything(here)
12
- @here = here
13
- end
14
- end
15
-
16
- module WithContent
17
- def sumthin
18
- class_eval('1 + 1')
19
- end
20
- end
21
- CODE
22
-
9
+ @okay_file = %{
10
+ class Whatever < Set
11
+ def okaything(here)
12
+ @here = here
13
+ end
14
+ end
15
+
16
+ module WithContent
17
+ def sumthin
18
+ class_eval('1 + 1')
19
+ end
20
+
21
+ def okishthing
22
+ end
23
+ end
24
+ }
23
25
  end
24
26
 
25
27
 
26
28
 
27
29
  it "correctly builds rule for class var" do
28
30
  code1 = "@@"
29
- rule1 = LawDSL.parse(code1)
31
+ rule1 = Rule.new(code1)
30
32
 
31
- class_var_file = <<CODE
32
- def badthing(here)
33
- @@here = here
34
- end
35
- CODE
33
+ class_var_file = %{
34
+ def badthing(here)
35
+ @@here = here
36
+ end
37
+ }
36
38
 
37
- class_result = rule1.call(class_var_file)
38
- result1a = rule1.call(@okay_file)
39
+ class_result = rule1.violation?(class_var_file)
40
+ result1a = rule1.violation?(@okay_file)
39
41
  class_result .must_equal true
40
42
  end
41
43
 
42
44
  it "correctly builds rule for protected" do
43
45
  code2 = "protected"
44
- rule2 = LawDSL.parse(code2)
46
+ rule2 = Rule.new(code2)
45
47
 
46
- protected_file = <<CODE
47
- class Whatever
48
- protected
49
- def not_really
50
- :false
51
- end
52
- end
53
- CODE
48
+ protected_file = %{
49
+ class Whatever
50
+ protected
51
+ def not_really
52
+ :false
53
+ end
54
+ end
55
+ }
54
56
 
55
- protected_result= rule2.call(protected_file)
56
- result2a = rule2.call(@okay_file)
57
+ protected_result= rule2.violation?(protected_file)
58
+ result2a = rule2.violation?(@okay_file)
57
59
 
58
60
  protected_result.must_equal true
59
61
  result2a .must_equal false
@@ -62,16 +64,16 @@ CODE
62
64
 
63
65
  it "correctly builds rule for eval" do
64
66
  code3 = "eval"
65
- rule3 = LawDSL.parse(code3)
67
+ rule3 = Rule.new(code3)
66
68
 
67
- eval_file = <<CODE
68
- def not_really
69
- eval('1 + 1')
70
- end
71
- CODE
69
+ eval_file = %{
70
+ def not_really
71
+ eval('1 + 1')
72
+ end
73
+ }
72
74
 
73
- eval_result = rule3.call(eval_file)
74
- result3a = rule3.call(@okay_file)
75
+ eval_result = rule3.violation?(eval_file)
76
+ result3a = rule3.violation?(@okay_file)
75
77
 
76
78
  eval_result.must_equal true
77
79
  result3a.must_equal false
@@ -80,15 +82,15 @@ CODE
80
82
 
81
83
  it "correctly builds rule for module" do
82
84
  code4 = "module :name end"
83
- rule4 = LawDSL.parse(code4)
85
+ rule4 = Rule.new(code4)
84
86
 
85
- module_file = <<CODE
86
- module Thing
87
- end
88
- CODE
87
+ module_file = %{
88
+ module Thing
89
+ end
90
+ }
89
91
 
90
- module_result = rule4.call(module_file)
91
- result4a = rule4.call(@okay_file)
92
+ module_result = rule4.violation?(module_file)
93
+ result4a = rule4.violation?(@okay_file)
92
94
 
93
95
  module_result.must_equal true
94
96
  result4a.must_equal false
@@ -96,75 +98,73 @@ CODE
96
98
 
97
99
  it "correctly builds rule for core" do
98
100
  code5 = "class :symbol < :core_class"
99
- rule5 = LawDSL.parse(code5)
100
-
101
- core_file = <<CODE
102
- class Whatever < String
103
- def badthing(here)
104
- @here = here
105
- end
106
- end
107
- CODE
108
- core_result = rule5.call(core_file)
109
- result5a = rule5.call(@okay_file)
101
+ rule5 = Rule.new(code5)
102
+
103
+ core_file = %{
104
+ class Whatever < String
105
+ def badthing(here)
106
+ @here = here
107
+ end
108
+ end
109
+ }
110
+ core_result = rule5.violation?(core_file)
111
+ result5a = rule5.violation?(@okay_file)
110
112
  core_result .must_equal true
111
113
  result5a.must_equal false
112
114
  end
113
115
 
114
-
115
- # it "correctly builds rule for unless else" do
116
- # code6 = "unless :symbols
117
- # :disjoint_code_seperator
118
- # else :more_symbols"
119
- # rule6 = LawDSL.parse(code6)
120
-
121
- # core_file = <<CODE
122
- # class Whatever < String
123
- # def badthing(here)
124
- # @here = here
125
- # end
126
- # end
127
- # CODE
128
-
129
116
  it "correctly builds rule for rescue nil" do
130
117
  code7 = "rescue nil"
131
- rule7 = LawDSL.parse(code7)
132
-
133
- nil_file = <<CODE
134
- begin
135
- "hi"
136
- rescue nil
137
- "bye"
138
- end
139
- CODE
140
- nil_result = rule7.call(nil_file)
141
- result7a = rule7.call(@okay_file)
118
+ rule7 = Rule.new(code7)
119
+
120
+ nil_file = %{
121
+ begin
122
+ "hi"
123
+ rescue nil
124
+ "bye"
125
+ end
126
+ }
127
+ nil_result = rule7.violation?(nil_file)
128
+ result7a = rule7.violation?(@okay_file)
142
129
  nil_result .must_equal true
143
130
  result7a.must_equal false
144
131
  end
145
132
 
146
133
  it "correctly builds rule for inherit struct.new" do
147
134
  code8 = "class :symbol < Struct.new"
148
- rule8 = LawDSL.parse(code8)
135
+ rule8 = Rule.new(code8)
149
136
 
150
- struct_file = <<CODE
151
- class MyClass < Struct.new("Customer", :name, :address)
152
- CODE
137
+ struct_file = %{
138
+ class MyClass < Struct.new("Customer", :name, :address)
139
+ }
153
140
 
154
- struct_result = rule8.call(struct_file)
155
- result8a = rule8.call(@okay_file)
141
+ struct_result = rule8.violation?(struct_file)
142
+ result8a = rule8.violation?(@okay_file)
156
143
 
157
144
  struct_result.must_equal true
158
145
  result8a.must_equal false
159
146
  end
160
147
 
161
-
162
- # end
163
-
164
- it "returns a hash with key counts and nil placeholders" do
165
- params = Rule.send(:params_count_hash, [/module/, :token1, :token2, :token1, /class/, :token3, /end/])
166
- params.keys.size.must_equal 3
167
- params[:token1].last.must_equal 2
148
+ it "correctly builds whitespace sensitive rule" do
149
+ code9 = ":vertical_whitespace_sensitive end\ndef"
150
+ rule9 = Rule.new(code9)
151
+
152
+ def_file = %{
153
+ class Mine
154
+ def method
155
+ true
156
+ end
157
+ def method2
158
+ false
159
+ end
160
+ end
161
+ }
162
+
163
+ def_result = rule9.violation?(def_file)
164
+ result9a = rule9.violation?(@okay_file)
165
+
166
+ def_result.must_equal true
167
+ result9a.must_equal false
168
168
  end
169
169
 
170
170
  it "returns a hash with key counts and nil placeholders" do
@@ -174,7 +174,7 @@ CODE
174
174
  end
175
175
 
176
176
  it "returns a block from build_block method" do
177
- block = LawDSL.send(:build_block,"@@")
177
+ block = LawParser.send(:build_block,"@@")
178
178
  assert_kind_of Proc, block
179
179
  end
180
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: outlaw
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-25 00:00:00.000000000 Z
12
+ date: 2012-02-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70266835445120 !ruby/object:Gem::Requirement
16
+ requirement: &20326092 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.9.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70266835445120
24
+ version_requirements: *20326092
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: minitest
27
- requirement: &70266835444560 !ruby/object:Gem::Requirement
27
+ requirement: &20325792 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70266835444560
35
+ version_requirements: *20325792
36
36
  description: ! " Keep bad code out of your projects. Your idea of bad code, no
37
37
  one elses.\n\n Outlaw defines an example based DSL for demonstrating anti-patterns
38
38
  and\n builds a rule for each anti-pattern that it alerts the user to violations\n
@@ -56,8 +56,9 @@ files:
56
56
  - examples/.outlawed.example
57
57
  - lib/outlaw.rb
58
58
  - lib/outlaw/enforcement.rb
59
- - lib/outlaw/law_dsl.rb
59
+ - lib/outlaw/law_parser.rb
60
60
  - lib/outlaw/rule.rb
61
+ - lib/outlaw/rule_methods.rb
61
62
  - lib/outlaw/version.rb
62
63
  - outlaw.gemspec
63
64
  - test/outlaw_test.rb
@@ -1,69 +0,0 @@
1
- module Outlaw
2
- module LawDSL
3
- class << self
4
- def parse(restriction, message="")
5
- tokens = restriction.split
6
- parsed_restriction = []
7
- tokens.each do |token|
8
- case
9
- when special_case?(token)
10
- next #TODO - handle AST branches/disjoint code/meta-symbols to be defined
11
- when multipart?(token) #this handles multi-token literals, Const.new etc
12
- parsed_restriction += Ripper.lex(token)
13
- .reduce([]){|array, tkn|
14
- array << token_type_regex(tkn) }
15
- when defined_collection?(token)
16
- parsed_restriction << Outlaw.const_get(string_to_sym(token.upcase))
17
- when parameter?(token)
18
- parsed_restriction << string_to_sym(token)
19
- else
20
- parsed_restriction += build_regex(token)
21
- end
22
- end
23
- Rule.new(message, restriction, &build_block(parsed_restriction))
24
- end
25
-
26
- private
27
-
28
- def token_type_regex(token)
29
- /#{token[2]}/
30
- end
31
-
32
- def parameter?(token)
33
- token[0].chr == ':'
34
- end
35
-
36
- def special_case?(token)
37
- SPECIAL_CASES.include? token
38
- end
39
-
40
- def defined_collection?(token)
41
- parameter?(token) && Outlaw.const_defined?(string_to_sym(token.upcase))
42
- end
43
-
44
- def string_to_sym(str)
45
- str[1..-1].to_sym
46
- end
47
-
48
- def build_regex(token)
49
- #fully expect this hack to come back & haunt me, but passes curr. examples
50
- [/\A#{token}/]
51
- end
52
-
53
- def multipart?(token)
54
- !parameter?(token) && Ripper.lex(token).count > 1
55
- end
56
-
57
- def build_block(pattern)
58
- lambda do |file|
59
- program = Ripper.tokenize(file)
60
- program.each_with_index do |token, index|
61
- next unless token.match(pattern.first)
62
- return true if Rule.test(program, index, pattern)
63
- end
64
- return false
65
- end
66
- end
67
- end
68
- end
69
- end