rspec-kickstarter 0.0.9 → 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.
@@ -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: []