kevinrutherford-reek 0.3.1.4

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 (70) hide show
  1. data/History.txt +92 -0
  2. data/README.txt +6 -0
  3. data/Rakefile +7 -0
  4. data/bin/reek +19 -0
  5. data/lib/reek/block_context.rb +37 -0
  6. data/lib/reek/class_context.rb +73 -0
  7. data/lib/reek/code_context.rb +47 -0
  8. data/lib/reek/code_parser.rb +204 -0
  9. data/lib/reek/exceptions.reek +13 -0
  10. data/lib/reek/if_context.rb +25 -0
  11. data/lib/reek/method_context.rb +85 -0
  12. data/lib/reek/module_context.rb +34 -0
  13. data/lib/reek/name.rb +42 -0
  14. data/lib/reek/object_refs.rb +53 -0
  15. data/lib/reek/options.rb +92 -0
  16. data/lib/reek/rake_task.rb +121 -0
  17. data/lib/reek/report.rb +42 -0
  18. data/lib/reek/sexp_formatter.rb +52 -0
  19. data/lib/reek/singleton_method_context.rb +27 -0
  20. data/lib/reek/smell_warning.rb +49 -0
  21. data/lib/reek/smells/control_couple.rb +61 -0
  22. data/lib/reek/smells/duplication.rb +50 -0
  23. data/lib/reek/smells/feature_envy.rb +58 -0
  24. data/lib/reek/smells/large_class.rb +50 -0
  25. data/lib/reek/smells/long_method.rb +43 -0
  26. data/lib/reek/smells/long_parameter_list.rb +43 -0
  27. data/lib/reek/smells/long_yield_list.rb +18 -0
  28. data/lib/reek/smells/nested_iterators.rb +28 -0
  29. data/lib/reek/smells/smell_detector.rb +66 -0
  30. data/lib/reek/smells/smells.rb +85 -0
  31. data/lib/reek/smells/uncommunicative_name.rb +80 -0
  32. data/lib/reek/smells/utility_function.rb +34 -0
  33. data/lib/reek/source.rb +116 -0
  34. data/lib/reek/spec.rb +130 -0
  35. data/lib/reek/stop_context.rb +62 -0
  36. data/lib/reek/yield_call_context.rb +14 -0
  37. data/lib/reek.rb +8 -0
  38. data/spec/integration/reek_source_spec.rb +20 -0
  39. data/spec/integration/script_spec.rb +55 -0
  40. data/spec/reek/class_context_spec.rb +198 -0
  41. data/spec/reek/code_context_spec.rb +92 -0
  42. data/spec/reek/code_parser_spec.rb +44 -0
  43. data/spec/reek/config_spec.rb +42 -0
  44. data/spec/reek/module_context_spec.rb +38 -0
  45. data/spec/reek/object_refs_spec.rb +129 -0
  46. data/spec/reek/options_spec.rb +13 -0
  47. data/spec/reek/report_spec.rb +48 -0
  48. data/spec/reek/sexp_formatter_spec.rb +31 -0
  49. data/spec/reek/singleton_method_context_spec.rb +17 -0
  50. data/spec/reek/smells/control_couple_spec.rb +23 -0
  51. data/spec/reek/smells/duplication_spec.rb +81 -0
  52. data/spec/reek/smells/feature_envy_spec.rb +129 -0
  53. data/spec/reek/smells/large_class_spec.rb +86 -0
  54. data/spec/reek/smells/long_method_spec.rb +59 -0
  55. data/spec/reek/smells/long_parameter_list_spec.rb +92 -0
  56. data/spec/reek/smells/nested_iterators_spec.rb +33 -0
  57. data/spec/reek/smells/smell_spec.rb +24 -0
  58. data/spec/reek/smells/uncommunicative_name_spec.rb +118 -0
  59. data/spec/reek/smells/utility_function_spec.rb +96 -0
  60. data/spec/samples/inline.rb +704 -0
  61. data/spec/samples/inline_spec.rb +40 -0
  62. data/spec/samples/optparse.rb +1788 -0
  63. data/spec/samples/optparse_spec.rb +100 -0
  64. data/spec/samples/redcloth.rb +1130 -0
  65. data/spec/samples/redcloth_spec.rb +93 -0
  66. data/spec/spec.opts +1 -0
  67. data/spec/spec_helper.rb +15 -0
  68. data/tasks/reek.rake +20 -0
  69. data/tasks/rspec.rake +22 -0
  70. metadata +167 -0
data/History.txt ADDED
@@ -0,0 +1,92 @@
1
+ == 0.3.x 2009-??-??
2
+
3
+ === Major enhancements:
4
+
5
+ * Place *.reek files in source folder to configure smell detection behaviour
6
+ * Added -f option to configure report format
7
+ * --sort_order replaced by -f, -c and -s
8
+ * Matchers provided for rspec; eg. foo.should_not reek
9
+
10
+ === Minor enhancements:
11
+
12
+ * Smells in singleton methods are now analysed
13
+ * Uncommunicative parameter names in blocks now reported
14
+ * Modules and blocks now reflected in scope of smell reports
15
+
16
+ === Fixes:
17
+
18
+ * Corrected false reports of long arg lists to yield
19
+ * A method can now be a UtilityFunction only when it includes a call
20
+ * Removed the website from the gem [#12]
21
+
22
+ == 0.3.1 2008-11-17
23
+
24
+ * Minor enhancements:
25
+ * Uncommunicative Name now checks instance variables more thoroughly
26
+ * Uncommunicative Name now warns about names of the form 'x2'
27
+ * Added check for duplicated calls within a method
28
+ * Reduced scope of Feature Envy warnings to cover only overuse of lvars
29
+ * Added rdoc comments explaining what each smell is about
30
+ * Chained iterators are no longer mis-reported as nested
31
+
32
+ == 0.3.0 2008-11-02
33
+
34
+ * Minor enhancements:
35
+ * New smell: first naive checks for Control Couple
36
+ * reek now only checks sources passed on the command line
37
+ * Code snippets can be supplied on the commandline
38
+ * Added headings and warnings count when smells in multiple files
39
+ * Added Reek::RakeTask to run reek from rakefiles
40
+ * Tweaks:
41
+ * Fixed: Returns exit status 2 when smells are reported
42
+ * Fixed: no longer claims an empty method is a Utility Function
43
+
44
+ == 0.2.3 2008-09-22
45
+
46
+ * Minor enhancements:
47
+ * Only reports Feature Envy when the method isn't a Utility Function
48
+ * General improvements to assessing Feature Envy
49
+ * Tweaks:
50
+ * Fixed: coping with parameterless yield call
51
+ * Fixed: copes with :self as an expression
52
+ * Fixed: displaying the receiver of many more kinds of Feature Envy
53
+ * Fixed: Large Class calculation for Object
54
+
55
+ == 0.2.2 2008-09-15
56
+
57
+ * Tweaks:
58
+ * Fixed --version!
59
+
60
+ == 0.2.1 2008-09-14
61
+
62
+ * Tweaks:
63
+ * Now works from the source code, instead of requiring each named file
64
+ * Added integration tests that run reek on a couple of gems
65
+
66
+ == 0.2.0 2008-09-10
67
+
68
+ * Minor enhancements:
69
+ * Added --help, --version options
70
+ * Added --sort option to sort the report by smell or by code location
71
+
72
+ == 0.1.1 2008-09-09
73
+
74
+ * Some tweaks:
75
+ * Fixed report printing for Feature Envy when the receiver is a block
76
+ * Fixed: successive iterators reported as nested
77
+ * Fixed: Long Method now reports the total length of the method
78
+ * Fixed: each smell reported only once
79
+
80
+ == 0.1.0 2008-09-09
81
+
82
+ * 1 minor enhancement:
83
+ * Added a check for nested iterators within a method
84
+ * Some tweaks:
85
+ * Begun adding some rdoc
86
+ * Split some of the specs into more meaningful chunks
87
+ * Updated the rakefile so that rcov is no longer the default
88
+
89
+ == 0.0.1 2008-09-08
90
+
91
+ * 1 major enhancement:
92
+ * Initial release
data/README.txt ADDED
@@ -0,0 +1,6 @@
1
+ = Reek
2
+
3
+ Code smell detection for Ruby.
4
+
5
+ The documentation is at http://wiki.github.com/kevinrutherford/reek
6
+ The code lives at http://github.com/kevinrutherford/reek/tree
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'fileutils'
4
+
5
+ $:.unshift File.dirname(__FILE__) + '/lib'
6
+
7
+ Dir['tasks/**/*.rake'].each { |t| load t }
data/bin/reek ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Reek examines Ruby source code for smells.
4
+ # Visit http://reek.rubyforge.org/ for docs etc.
5
+ #
6
+ # Author: Kevin Rutherford
7
+ #
8
+
9
+ require 'reek'
10
+ require 'reek/source'
11
+ require 'reek/options'
12
+
13
+ exitstatus = 0
14
+ source = Reek::Options.parse(ARGV)
15
+ if source.smelly?
16
+ puts source.report
17
+ exitstatus = 2
18
+ end
19
+ exit(exitstatus)
@@ -0,0 +1,37 @@
1
+ require 'reek/code_context'
2
+
3
+ module Reek
4
+ class BlockContext < CodeContext
5
+
6
+ def initialize(outer, exp)
7
+ super
8
+ @parameters = []
9
+ @local_variables = []
10
+ @name = Name.new('block')
11
+ end
12
+
13
+ def inside_a_block?
14
+ true
15
+ end
16
+
17
+ def has_parameter(name)
18
+ @parameters.include?(name) or @outer.has_parameter(name)
19
+ end
20
+
21
+ def nested_block?
22
+ @outer.inside_a_block?
23
+ end
24
+
25
+ def record_parameter(sym)
26
+ @parameters << Name.new(sym)
27
+ end
28
+
29
+ def outer_name
30
+ "#{@outer.outer_name}#{@name}/"
31
+ end
32
+
33
+ def variable_names
34
+ @parameters + @local_variables
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,73 @@
1
+ require 'reek/code_context'
2
+
3
+ class Class
4
+ def non_inherited_methods
5
+ instance_methods(false) + private_instance_methods(false)
6
+ end
7
+
8
+ def is_overriding_method?(sym)
9
+ instance_methods(false).include?(sym) and superclass.instance_methods(true).include?(sym)
10
+ end
11
+ end
12
+
13
+ module Reek
14
+ class ClassContext < CodeContext
15
+
16
+ def ClassContext.create(outer, exp)
17
+ res = Name.resolve(exp[1], outer)
18
+ ClassContext.new(res[0], res[1], exp[2])
19
+ end
20
+
21
+ def initialize(outer, name, superclass = nil)
22
+ super(outer, nil)
23
+ @name = name
24
+ @superclass = superclass
25
+ @parsed_methods = []
26
+ @instance_variables = []
27
+ end
28
+
29
+ def myself
30
+ @myself ||= @outer.find_module(@name)
31
+ end
32
+
33
+ def find_module(modname)
34
+ sym = modname.to_s
35
+ return nil unless myself
36
+ myself.const_defined?(sym) ? myself.const_get(sym) : nil
37
+ end
38
+
39
+ def is_overriding_method?(name)
40
+ return false unless myself
41
+ myself.is_overriding_method?(name.to_s)
42
+ end
43
+
44
+ def is_struct?
45
+ @superclass == [:const, :Struct]
46
+ end
47
+
48
+ def num_methods
49
+ meths = myself ? myself.non_inherited_methods : @parsed_methods
50
+ meths.length
51
+ end
52
+
53
+ def record_instance_variable(sym)
54
+ @instance_variables << Name.new(sym)
55
+ end
56
+
57
+ def record_method(name)
58
+ @parsed_methods << name.to_s
59
+ end
60
+
61
+ def outer_name
62
+ "#{@outer.outer_name}#{@name}#"
63
+ end
64
+
65
+ def to_s
66
+ "#{@outer.outer_name}#{@name}"
67
+ end
68
+
69
+ def variable_names
70
+ @instance_variables
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ module Reek
2
+
3
+ #
4
+ # Superclass for all types of source code context. Each instance represents
5
+ # a code element of some kind, and each provides behaviour relevant to that
6
+ # code element. CodeContexts form a tree in the same way the code does,
7
+ # with each context holding a reference to a unique outer context.
8
+ #
9
+ class CodeContext
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(outer, exp)
14
+ @outer = outer
15
+ @exp = exp
16
+ @myself = nil
17
+ end
18
+
19
+ def matches?(strings)
20
+ me = @name.to_s
21
+ strings.any? do |str|
22
+ re = /#{str}/
23
+ re === me or re === self.to_s
24
+ end
25
+ end
26
+
27
+ #
28
+ # Bounces messages up the context tree to the first enclosing context
29
+ # that knows how to deal with the request.
30
+ #
31
+ def method_missing(method, *args)
32
+ @outer.send(method, *args)
33
+ end
34
+
35
+ def num_methods
36
+ 0
37
+ end
38
+
39
+ def outer_name
40
+ "#{@name}/"
41
+ end
42
+
43
+ def to_s
44
+ "#{@outer.outer_name}#{@name}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,204 @@
1
+ require 'rubygems'
2
+ require 'parse_tree'
3
+ require 'sexp_processor'
4
+ require 'reek/block_context'
5
+ require 'reek/class_context'
6
+ require 'reek/module_context'
7
+ require 'reek/stop_context'
8
+ require 'reek/if_context'
9
+ require 'reek/method_context'
10
+ require 'reek/singleton_method_context'
11
+ require 'reek/yield_call_context'
12
+
13
+ module Reek
14
+
15
+ class CodeParser < SexpProcessor
16
+
17
+ def self.parse_tree_for(code) # :nodoc:
18
+ ParseTree.new.parse_tree_for_string(code)
19
+ end
20
+
21
+ # Creates a new Ruby code checker. Any smells discovered by
22
+ # +check_source+ or +check_object+ will be stored in +report+.
23
+ def initialize(report, smells, ctx = StopContext.new)
24
+ super()
25
+ @report = report
26
+ @smells = smells
27
+ @element = ctx
28
+ @unsupported -= [:cfunc]
29
+ @default_method = :process_default
30
+ @require_empty = @warn_on_default = false
31
+ end
32
+
33
+ # Analyses the given Ruby source +code+ looking for smells.
34
+ # Any smells found are saved in the +Report+ object that
35
+ # was passed to this object's constructor.
36
+ def check_source(code)
37
+ check_parse_tree(CodeParser.parse_tree_for(code))
38
+ end
39
+
40
+ # Analyses the given Ruby object +obj+ looking for smells.
41
+ # Any smells found are saved in the +Report+ object that
42
+ # was passed to this object's constructor.
43
+ def check_object(obj)
44
+ check_parse_tree(ParseTree.new.parse_tree(obj))
45
+ end
46
+
47
+ def process_default(exp)
48
+ exp[1..-1].each { |sub| process(sub) if Array === sub }
49
+ s(exp)
50
+ end
51
+
52
+ def process_module(exp)
53
+ push(ModuleContext.create(@element, exp)) do
54
+ process_default(exp)
55
+ check_smells(:module)
56
+ end
57
+ s(exp)
58
+ end
59
+
60
+ def process_class(exp)
61
+ push(ClassContext.create(@element, exp)) do
62
+ process_default(exp) unless @element.is_struct?
63
+ check_smells(:class)
64
+ end
65
+ s(exp)
66
+ end
67
+
68
+ def process_defn(exp)
69
+ handle_context(MethodContext, :defn, exp)
70
+ end
71
+
72
+ def process_defs(exp)
73
+ handle_context(SingletonMethodContext, :defs, exp)
74
+ end
75
+
76
+ def process_args(exp)
77
+ exp[1..-1].each {|sym| @element.record_parameter(sym) }
78
+ s(exp)
79
+ end
80
+
81
+ def process_attrset(exp)
82
+ @element.record_depends_on_self if /^@/ === exp[1].to_s
83
+ s(exp)
84
+ end
85
+
86
+ def process_lit(exp)
87
+ val = exp[1]
88
+ @element.record_depends_on_self if val == :self
89
+ s(exp)
90
+ end
91
+
92
+ def process_iter(exp)
93
+ process(exp[1])
94
+ handle_context(BlockContext, :iter, exp[1..-1])
95
+ end
96
+
97
+ def process_dasgn_curr(exp)
98
+ @element.record_parameter(exp[1])
99
+ process_default(exp)
100
+ end
101
+
102
+ def process_block(exp)
103
+ @element.count_statements(CodeParser.count_statements(exp))
104
+ process_default(exp)
105
+ end
106
+
107
+ def process_yield(exp)
108
+ handle_context(YieldCallContext, :yield, exp)
109
+ end
110
+
111
+ def process_call(exp)
112
+ @element.record_call_to(exp)
113
+ process_default(exp)
114
+ end
115
+
116
+ def process_fcall(exp)
117
+ @element.record_depends_on_self
118
+ @element.refs.record_reference_to_self
119
+ process_default(exp)
120
+ end
121
+
122
+ def process_cfunc(exp)
123
+ @element.record_depends_on_self
124
+ s(exp)
125
+ end
126
+
127
+ def process_vcall(exp)
128
+ @element.record_depends_on_self
129
+ @element.refs.record_reference_to_self
130
+ s(exp)
131
+ end
132
+
133
+ def process_if(exp)
134
+ handle_context(IfContext, :if, exp)
135
+ end
136
+
137
+ def process_ivar(exp)
138
+ process_iasgn(exp)
139
+ end
140
+
141
+ def process_lasgn(exp)
142
+ @element.record_local_variable(exp[1])
143
+ process(exp[2])
144
+ s(exp)
145
+ end
146
+
147
+ def process_iasgn(exp)
148
+ @element.record_instance_variable(exp[1])
149
+ @element.record_depends_on_self
150
+ process_default(exp)
151
+ end
152
+
153
+ def process_self(exp)
154
+ @element.record_depends_on_self
155
+ s(exp)
156
+ end
157
+
158
+ private
159
+
160
+ def self.count_statements(exp)
161
+ stmts = exp[1..-1]
162
+ ignore = 0
163
+ ignore = 1 if is_expr?(stmts[0], :args)
164
+ ignore += 1 if stmts[1] == s(:nil)
165
+ stmts.length - ignore
166
+ end
167
+
168
+ def self.is_expr?(exp, type)
169
+ Array === exp and exp[0] == type
170
+ end
171
+
172
+ def self.is_global_variable?(exp)
173
+ is_expr?(exp, :gvar)
174
+ end
175
+
176
+ def handle_context(klass, type, exp)
177
+ push(klass.new(@element, exp)) do
178
+ process_default(exp)
179
+ check_smells(type)
180
+ end
181
+ s(exp)
182
+ end
183
+
184
+ def check_smells(type)
185
+ @smells[type].each {|smell| smell.examine(@element, @report) }
186
+ end
187
+
188
+ def push(context)
189
+ orig = @element
190
+ @element = context
191
+ yield
192
+ @element = orig
193
+ end
194
+
195
+ def pop(exp)
196
+ @element = @element.outer
197
+ s(exp)
198
+ end
199
+
200
+ def check_parse_tree(sexp) # :nodoc:
201
+ sexp.each { |exp| process(exp) }
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,13 @@
1
+ ---
2
+ FeatureEnvy:
3
+ exclude:
4
+ - examine_context
5
+ LargeClass:
6
+ exclude:
7
+ - CodeParser
8
+ LongMethod:
9
+ exclude:
10
+ - Reek::SexpFormatter#self.format
11
+ UtilityFunction:
12
+ exclude:
13
+ - Reek::Spec
@@ -0,0 +1,25 @@
1
+ require 'reek/code_context'
2
+
3
+ module Reek
4
+ class IfContext < CodeContext
5
+ attr_reader :if_expr
6
+
7
+ def initialize(outer, exp)
8
+ @outer = outer
9
+ @exp = exp
10
+ @if_expr = exp[1]
11
+ end
12
+
13
+ def tests_a_parameter?
14
+ @if_expr[0] == :lvar and has_parameter(@if_expr[1])
15
+ end
16
+
17
+ def outer_name
18
+ @outer.outer_name
19
+ end
20
+
21
+ def to_s
22
+ @outer.to_s
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,85 @@
1
+ require 'reek/name'
2
+ require 'reek/code_context'
3
+ require 'reek/object_refs'
4
+
5
+ module Reek
6
+ class MethodContext < CodeContext
7
+ attr_reader :parameters
8
+ attr_reader :calls
9
+ attr_reader :refs
10
+ attr_reader :num_statements
11
+
12
+ def initialize(outer, exp, record = true)
13
+ super(outer, exp)
14
+ @parameters = []
15
+ @local_variables = []
16
+ @name = Name.new(exp[1])
17
+ @num_statements = 0
18
+ @calls = Hash.new(0)
19
+ @depends_on_self = false
20
+ @refs = ObjectRefs.new
21
+ @outer.record_method(@name) # TODO: should be children of outer?
22
+ end
23
+
24
+ def count_statements(num)
25
+ @num_statements += num
26
+ end
27
+
28
+ def depends_on_instance?
29
+ @depends_on_self || is_overriding_method?(@name)
30
+ end
31
+
32
+ def has_parameter(sym)
33
+ @parameters.include?(sym.to_s)
34
+ end
35
+
36
+ def record_call_to(exp)
37
+ @calls[exp] += 1
38
+ receiver, meth = exp[1..2]
39
+ if receiver.nil?
40
+ record_depends_on_self
41
+ else
42
+ case receiver[0]
43
+ when :lvar
44
+ @refs.record_ref(receiver) unless meth == :new
45
+ when :ivar
46
+ record_depends_on_self
47
+ @refs.record_reference_to_self
48
+ end
49
+ end
50
+ end
51
+
52
+ def record_depends_on_self
53
+ @depends_on_self = true
54
+ end
55
+
56
+ def record_local_variable(sym)
57
+ @local_variables << Name.new(sym)
58
+ end
59
+
60
+ def self.is_block_arg?(param)
61
+ Array === param and param[0] == :block
62
+ end
63
+
64
+ def record_parameter(param)
65
+ @parameters << Name.new(param) unless MethodContext.is_block_arg?(param)
66
+ end
67
+
68
+ def outer_name
69
+ "#{@outer.outer_name}#{@name}/"
70
+ end
71
+
72
+ def to_s
73
+ "#{@outer.outer_name}#{@name}"
74
+ end
75
+
76
+ def envious_receivers
77
+ return [] if @refs.self_is_max?
78
+ @refs.max_keys
79
+ end
80
+
81
+ def variable_names
82
+ @parameters + @local_variables
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,34 @@
1
+ require 'reek/code_context'
2
+
3
+ module Reek
4
+ class ModuleContext < CodeContext
5
+
6
+ def ModuleContext.create(outer, exp)
7
+ res = Name.resolve(exp[1], outer)
8
+ ModuleContext.new(res[0], res[1])
9
+ end
10
+
11
+ def initialize(outer, name)
12
+ super(outer, nil)
13
+ @name = name
14
+ end
15
+
16
+ def myself
17
+ @myself ||= @outer.find_module(@name)
18
+ end
19
+
20
+ def find_module(modname)
21
+ return nil unless myself
22
+ sym = modname.to_s
23
+ myself.const_defined?(sym) ? myself.const_get(sym) : nil
24
+ end
25
+
26
+ def outer_name
27
+ "#{@outer.outer_name}#{@name}::"
28
+ end
29
+
30
+ def variable_names
31
+ []
32
+ end
33
+ end
34
+ end
data/lib/reek/name.rb ADDED
@@ -0,0 +1,42 @@
1
+ module Reek
2
+ class Name
3
+ include Comparable
4
+
5
+ def self.resolve(exp, context)
6
+ return [context, new(exp)] unless Array === exp
7
+ name = exp[1]
8
+ case exp[0]
9
+ when :colon2
10
+ return [resolve(name, context)[0], new(exp[2])]
11
+ when :const
12
+ return [ModuleContext.create(context, exp), new(name)]
13
+ when :colon3
14
+ return [StopContext.new, new(name)]
15
+ else
16
+ return [context, new(name)]
17
+ end
18
+ end
19
+
20
+ def initialize(sym)
21
+ @name = sym.to_s
22
+ end
23
+
24
+ def hash # :nodoc:
25
+ @name.hash
26
+ end
27
+
28
+ def <=>(other) # :nodoc:
29
+ @name <=> other.to_s
30
+ end
31
+
32
+ alias eql? <=>
33
+
34
+ def effective_name
35
+ @name.gsub(/^@*/, '')
36
+ end
37
+
38
+ def to_s
39
+ @name
40
+ end
41
+ end
42
+ end