rspec-kickstarter 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,8 +12,8 @@ spec_dir = './spec'
12
12
 
13
13
  opt = OptionParser.new
14
14
  opt.on('-f', 'Create if absent or append to the existing spec') { |_| force_write = true }
15
- opt.on('-n', 'Dry run mode') { |_| dry_run = true }
16
- opt.on('-o VAL', 'Output directory') { |dir| spec_dir = dir }
15
+ opt.on('-n', 'Dry run mode (shows generated code to console)') { |_| dry_run = true }
16
+ opt.on('-o VAL', 'Output directory Output directory (default: ./spec)') { |dir| spec_dir = dir }
17
17
 
18
18
  args = opt.parse(ARGV)
19
19
  dir_or_file = args.first
@@ -4,128 +4,39 @@ require 'rdoc'
4
4
  require 'rdoc/parser/ruby'
5
5
  require 'rdoc/options'
6
6
  require 'rdoc/stats'
7
+ require 'rspec_kickstarter'
7
8
 
8
- module RSpecKickstarter
9
- class Generator
9
+ class RSpecKickstarter::Generator
10
10
 
11
- attr_accessor :spec_dir
11
+ attr_accessor :spec_dir
12
12
 
13
- def initialize(spec_dir = './spec')
14
- @spec_dir = spec_dir.gsub(/\/$/, '')
15
- end
16
-
17
- def get_target(top_level)
18
- c = top_level.classes.first
19
- if c.nil?
20
- m = top_level.modules.first
21
- if m.nil?
22
- top_level.is_a?(RDoc::NormalModule) ? top_level : nil
23
- else
24
- get_target(m)
25
- end
26
- else
27
- c
28
- end
29
- end
30
-
31
- def get_complete_class_name(c, name = c.name)
32
- if !c.parent.name.nil? && c.parent.is_a?(RDoc::NormalModule)
33
- get_complete_class_name(c.parent, "#{c.parent.name}::#{name}")
34
- else
35
- name
36
- end
37
- end
38
-
39
- def instance_name(c)
40
- c.name.gsub(/::/, '/').
41
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
42
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
43
- tr("-", "_").
44
- downcase
45
- end
46
-
47
- def to_param_names_array(params)
48
- params.split(',').map { |p| p.gsub(/[\(\)\s]/, '').gsub(/=.+$/, '') }.reject { |p| p.nil? || p.empty? }
49
- end
50
-
51
- def get_params_initialization_code(method)
52
- code = to_param_names_array(method.params).map { |p| " #{p} = stub('#{p}')" }.join("\n")
53
- code.empty? ? "" : "\n#{code}"
54
- end
55
-
56
- def get_instantiation_code(c, method)
57
- if method.singleton
58
- ""
59
- else
60
- constructor = c.method_list.find { |m| m.name == 'new' }
61
- if constructor.nil?
62
- "\n #{instance_name(c)} = #{get_complete_class_name(c)}.new"
63
- else
64
- get_params_initialization_code(constructor) +
65
- "\n #{instance_name(c)} = #{get_complete_class_name(c)}.new(#{to_param_names_array(constructor.params).join(', ')})"
66
- end
67
- end
68
- end
69
-
70
- def get_method_invocation_code(c, method)
71
- if method.singleton
72
- "#{get_complete_class_name(c)}.#{method.name}(#{to_param_names_array(method.params).join(', ')})#{get_block_code(method)}"
73
- else
74
- "#{instance_name(c)}.#{method.name}(#{to_param_names_array(method.params).join(', ')})#{get_block_code(method)}"
75
- end
76
- end
77
-
78
- def get_block_code(method)
79
- if method.block_params.nil? || method.block_params.empty?
80
- ""
81
- else
82
- " { |#{method.block_params}| }"
83
- end
84
- end
13
+ def initialize(spec_dir = './spec')
14
+ @spec_dir = spec_dir.gsub(/\/$/, '')
15
+ end
85
16
 
86
- def write_spec(file_path, force_write = false, dry_run = false)
17
+ def write_spec(file_path, force_write = false, dry_run = false)
87
18
 
88
- body = File.read(file_path)
19
+ top_level = get_ruby_parser(file_path).scan
20
+ c = extract_target_class_or_module(top_level)
89
21
 
90
- top_level = RDoc::TopLevel.new(file_path)
91
- if RUBY_VERSION.to_f < 2.0
92
- # reset is removed since 2.0
93
- RDoc::TopLevel.reset()
94
- end
22
+ if c.nil?
23
+ puts "#{file_path} skipped (Class/Module not found)."
24
+ else
95
25
 
96
- # RDoc::Stats initialization
97
- if RUBY_VERSION.to_f >= 2.0
98
- # Ruby 2.0 requires RDoc::Store internally.
99
- store = RDoc::Store.new
100
- top_level.store = store
101
- stats = RDoc::Stats.new(store, 1)
102
- else
103
- stats = RDoc::Stats.new(1)
104
- end
26
+ spec_path = get_spec_path(file_path)
105
27
 
106
- parser = RDoc::Parser::Ruby.new(
107
- top_level,
108
- file_path,
109
- body,
110
- RDoc::Options.new,
111
- stats
112
- )
113
- top_level = parser.scan
114
- c = get_target(top_level)
115
-
116
- if c.nil?
117
- puts "#{file_path} skipped (Class/Module not found)."
118
- else
28
+ if force_write && File.exist?(spec_path)
29
+ # Append to the existing spec or skip
119
30
 
120
- spec_path = spec_dir + '/' + file_path.gsub(/^(lib\/)|(app\/)/, '').gsub(/\.rb$/, '_spec.rb')
31
+ existing_spec = File.read(spec_path)
32
+ racking_methods = c.method_list
33
+ .select { |m| m.visibility == :public }
34
+ .reject { |m| existing_spec.match(m.name) }
121
35
 
122
- if force_write && File.exist?(spec_path)
123
- existing_spec = File.read(spec_path)
124
- racking_methods = c.method_list.select { |m| m.visibility == :public }.reject { |m| existing_spec.match(m.name) }
125
- if racking_methods.empty?
126
- puts "#{spec_path} skipped."
127
- else
128
- additional_spec = <<SPEC
36
+ if racking_methods.empty?
37
+ puts "#{spec_path} skipped."
38
+ else
39
+ additional_spec = <<SPEC
129
40
  #{racking_methods.map { |method|
130
41
  <<EACH_SPEC
131
42
  # TODO auto-generated
@@ -138,34 +49,38 @@ module RSpecKickstarter
138
49
  EACH_SPEC
139
50
  }.join("\n")}
140
51
  SPEC
141
- last_end_not_found = true
142
- code = existing_spec.split("\n").reverse.reject { |line|
143
- if last_end_not_found
144
- last_end_not_found = line.gsub(/#.+$/, '').strip != "end"
145
- true
146
- else
147
- false
148
- end
149
- }.reverse.join("\n") + "\n" + additional_spec + "\nend\n"
150
- if dry_run
151
- puts "----- #{spec_path} -----"
152
- puts code
52
+ last_end_not_found = true
53
+ code = existing_spec.split("\n").reverse.reject { |line|
54
+ if last_end_not_found
55
+ last_end_not_found = line.gsub(/#.+$/, '').strip != "end"
56
+ true
153
57
  else
154
- File.open(spec_path, 'w') { |f| f.write(code) }
58
+ false
155
59
  end
156
- puts "#{spec_path} modified."
60
+ }.reverse.join("\n") + "\n" + additional_spec + "\nend\n"
61
+ if dry_run
62
+ puts "----- #{spec_path} -----"
63
+ puts code
64
+ else
65
+ File.open(spec_path, 'w') { |f| f.write(code) }
157
66
  end
67
+ puts "#{spec_path} modified."
68
+ end
158
69
 
159
- else
160
- self_path = file_path.gsub(/^(lib\/)|(app\/)/, '').gsub(/\.rb$/, '')
161
- code = <<SPEC
70
+ else
71
+ # Create a new spec
72
+
73
+ self_path = to_string_value_to_require(file_path)
74
+ code = <<SPEC
162
75
  # -*- encoding: utf-8 -*-
163
76
  require 'spec_helper'
164
77
  require '#{self_path}'
165
78
 
166
79
  describe #{get_complete_class_name(c)} do
167
80
 
168
- #{c.method_list.select { |m| m.visibility == :public }.map { |method|
81
+ #{c.method_list
82
+ .select { |m| m.visibility == :public }
83
+ .map { |method|
169
84
  <<EACH_SPEC
170
85
  # TODO auto-generated
171
86
  describe '#{method.name}' do
@@ -178,24 +93,173 @@ EACH_SPEC
178
93
  }.join("\n")}
179
94
  end
180
95
  SPEC
181
-
182
- if dry_run
183
- puts "----- #{spec_path} -----"
184
- puts code
96
+ if dry_run
97
+ puts "----- #{spec_path} -----"
98
+ puts code
99
+ else
100
+ if File.exist?(spec_path)
101
+ puts "#{spec_path} already exists."
185
102
  else
186
- if File.exist?(spec_path)
187
- puts "#{spec_path} already exists."
188
- else
189
- FileUtils.mkdir_p(File.dirname(spec_path))
190
- File.open(spec_path, 'w') { |f| f.write(code) }
191
- puts "#{spec_path} created."
192
- end
103
+ FileUtils.mkdir_p(File.dirname(spec_path))
104
+ File.open(spec_path, 'w') { |f| f.write(code) }
105
+ puts "#{spec_path} created."
193
106
  end
194
107
  end
195
108
  end
109
+ end
110
+
111
+ end
112
+
113
+ #
114
+ # Creates new RDoc::Parser::Ruby instance.
115
+ #
116
+ def get_ruby_parser(file_path)
117
+ top_level = RDoc::TopLevel.new(file_path)
118
+ if RUBY_VERSION.to_f < 2.0
119
+ # reset is removed since 2.0
120
+ RDoc::TopLevel.reset()
121
+ end
122
+
123
+ # RDoc::Stats initialization
124
+ if RUBY_VERSION.to_f >= 2.0
125
+ # Ruby 2.0 requires RDoc::Store internally.
126
+ store = RDoc::Store.new
127
+ top_level.store = store
128
+ stats = RDoc::Stats.new(store, 1)
129
+ else
130
+ stats = RDoc::Stats.new(1)
131
+ end
132
+
133
+ RDoc::Parser::Ruby.new(
134
+ top_level,
135
+ file_path,
136
+ File.read(file_path),
137
+ RDoc::Options.new,
138
+ stats
139
+ )
140
+ end
141
+
142
+ #
143
+ # Extracts RDoc::NormalClass/RDoc::NormalModule from RDoc::TopLevel.
144
+ #
145
+ def extract_target_class_or_module(top_level)
146
+ c = top_level.classes.first
147
+ if c.nil?
148
+ m = top_level.modules.first
149
+ if m.nil?
150
+ top_level.is_a?(RDoc::NormalModule) ? top_level : nil
151
+ else
152
+ extract_target_class_or_module(m)
153
+ end
154
+ else
155
+ c
156
+ end
157
+ end
158
+
159
+ #
160
+ # Gets the complete class name from RDoc::NormalClass/RDoc::NormalModule instance.
161
+ #
162
+ def get_complete_class_name(c, name = c.name)
163
+ if !c.parent.name.nil? && c.parent.is_a?(RDoc::NormalModule)
164
+ get_complete_class_name(c.parent, "#{c.parent.name}::#{name}")
165
+ else
166
+ name
167
+ end
168
+ end
169
+
170
+ #
171
+ # Returns spec file path.
172
+ # e.g. "lib/foo/bar_baz.rb" -> "spec/foo/bar_baz_spec.rb"
173
+ #
174
+ def get_spec_path(file_path)
175
+ spec_dir + '/' + file_path.gsub(/^\.\//, '').gsub(/^(lib\/)|(app\/)/, '').gsub(/\.rb$/, '_spec.rb')
176
+ end
177
+
178
+ #
179
+ # Returns string value to require.
180
+ # e.g. "lib/foo/bar_baz.rb" -> "foo/bar_baz"
181
+ #
182
+ def to_string_value_to_require(file_path)
183
+ file_path.gsub(/^(lib\/)|(app\/)/, '').gsub(/\.rb$/, '')
184
+ end
185
+
186
+ #
187
+ # Returns snake_case name.
188
+ # e.g. FooBar -> "foo_bar"
189
+ #
190
+ def instance_name(c)
191
+ c.name
192
+ .gsub(/::/, '/')
193
+ .gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
194
+ .gsub(/([a-z\d])([A-Z])/,'\1_\2')
195
+ .tr("-", "_")
196
+ .downcase
197
+ end
198
+
199
+ #
200
+ # Extracts parameter names as an *Array*.
201
+ # e.g. "()" -> []
202
+ # e.g. "(a, b = 'foo')" -> ["a", "b"]
203
+ #
204
+ def to_param_names_array(params)
205
+ params.split(',').map { |p| p.gsub(/[\(\)\s]/, '').gsub(/=.+$/, '') }.reject { |p| p.nil? || p.empty? }
206
+ end
207
+
208
+ #
209
+ # Code generation
210
+ #
211
+
212
+ #
213
+ # e.g.
214
+ # a = stub('a')
215
+ # b = stub('b')
216
+ # bar_baz = BarBaz.new(a, b)
217
+ #
218
+ def get_instantiation_code(c, method)
219
+ if method.singleton
220
+ ""
221
+ else
222
+ constructor = c.method_list.find { |m| m.name == 'new' }
223
+ if constructor.nil?
224
+ "\n #{instance_name(c)} = #{get_complete_class_name(c)}.new"
225
+ else
226
+ get_params_initialization_code(constructor) +
227
+ "\n #{instance_name(c)} = #{get_complete_class_name(c)}.new(#{to_param_names_array(constructor.params).join(', ')})"
228
+ end
229
+ end
230
+ end
196
231
 
232
+ #
233
+ # e.g.
234
+ # a = stub('a')
235
+ # b = stub('b')
236
+ #
237
+ def get_params_initialization_code(method)
238
+ code = to_param_names_array(method.params).map { |p| " #{p} = stub('#{p}')" }.join("\n")
239
+ code.empty? ? "" : "\n#{code}"
240
+ end
241
+
242
+ #
243
+ # e.g. BarBaz.do_something(a, b) { |c| }
244
+ #
245
+ def get_method_invocation_code(c, method)
246
+ if method.singleton
247
+ "#{get_complete_class_name(c)}.#{method.name}(#{to_param_names_array(method.params).join(', ')})#{get_block_code(method)}"
248
+ else
249
+ "#{instance_name(c)}.#{method.name}(#{to_param_names_array(method.params).join(', ')})#{get_block_code(method)}"
197
250
  end
251
+ end
198
252
 
253
+ #
254
+ # e.g. { |a, b| }
255
+ #
256
+ def get_block_code(method)
257
+ if method.block_params.nil? || method.block_params.empty?
258
+ ""
259
+ else
260
+ " { |#{method.block_params}| }"
261
+ end
199
262
  end
263
+
200
264
  end
201
265
 
@@ -1,3 +1,3 @@
1
1
  module RSpecKickstarter
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,7 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
+ require "rspec_kickstarter/generator"
3
4
  require "rspec_kickstarter/version"
4
5
 
5
6
  module RSpecKickstarter
7
+
8
+ def self.write_spec(file_path, spec_dir, force_write = false, dry_run = false)
9
+ generator = RSpecKickstarter::Generator.new(spec_dir)
10
+ generator.write_spec(file_path, force_write, dry_run)
11
+ end
12
+
6
13
  end
7
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-kickstarter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,13 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-26 00:00:00.000000000 Z
12
+ date: 2013-04-28 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: RSpec kickstarter generates spec files for existing code.
14
+ description: rspec-kickstarter supports you writing tests for existing code.
15
15
  email:
16
- - - seratch@gmail.com
17
- - - MIT
18
- - RSpec kickstarter generates spec files for existing code.
16
+ - seratch@gmail.com
19
17
  executables:
20
18
  - rspec-kickstarter
21
19
  extensions: []
@@ -49,5 +47,5 @@ rubyforge_project:
49
47
  rubygems_version: 1.8.24
50
48
  signing_key:
51
49
  specification_version: 3
52
- summary: RSpec kickstarter generates spec files for existing code.
50
+ summary: rspec-kickstarter supports you writing tests for existing code.
53
51
  test_files: []