Class: RSpecKickstarter::Generator

Inherits:
Object
  • Object
show all
Includes:
ERBTemplates
Defined in:
lib/rspec_kickstarter/generator.rb

Constant Summary collapse

RAILS_RESOURCE_METHOD_AND_HTTP_METHOD =
{
  'index' => 'get',
  'new' => 'get',
  'create' => 'post',
  'show' => 'get',
  'edit' => 'get',
  'update' => 'put',
  'destroy' => 'delete'
}

Constants included from ERBTemplates

ERBTemplates::BASIC_METHODS_PART_TEMPLATE, ERBTemplates::BASIC_NEW_SPEC_TEMPLATE, ERBTemplates::RAILS_CONTROLLER_METHODS_PART_TEMPLATE, ERBTemplates::RAILS_CONTROLLER_NEW_SPEC_TEMPLATE, ERBTemplates::RAILS_HELPER_METHODS_PART_TEMPLATE, ERBTemplates::RAILS_HELPER_NEW_SPEC_TEMPLATE

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec_dir = './spec', delta_template = nil, full_template = nil) ⇒ Generator

Returns a new instance of Generator.



18
19
20
21
22
# File 'lib/rspec_kickstarter/generator.rb', line 18

def initialize(spec_dir = './spec', delta_template = nil, full_template = nil)
  @spec_dir = spec_dir.gsub(/\/$/, '')
  @delta_template = delta_template
  @full_template = full_template
end

Instance Attribute Details

#delta_templateObject

Returns the value of attribute delta_template.



16
17
18
# File 'lib/rspec_kickstarter/generator.rb', line 16

def delta_template
  @delta_template
end

#full_templateObject

Returns the value of attribute full_template.



16
17
18
# File 'lib/rspec_kickstarter/generator.rb', line 16

def full_template
  @full_template
end

#spec_dirObject

Returns the value of attribute spec_dir.



16
17
18
# File 'lib/rspec_kickstarter/generator.rb', line 16

def spec_dir
  @spec_dir
end

Instance Method Details

#append_to_existing_spec(class_or_module, dry_run, rails_mode, spec_path) ⇒ Object

Appends new tests to the existing spec.

rubocop:disable Metrics/AbcSize



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/rspec_kickstarter/generator.rb', line 135

def append_to_existing_spec(class_or_module, dry_run, rails_mode, spec_path)
  existing_spec = File.read(spec_path)
  lacking_methods = class_or_module.method_list.
    select { |m| m.visibility == :public }.
    reject { |m| existing_spec.match(m.name) }

  if lacking_methods.empty?
    puts "#{spec_path} skipped."
  else
    # These names are used in ERB template, don't delete.
    # rubocop:disable Lint/UselessAssignment
    methods_to_generate = lacking_methods
    c = class_or_module
    # rubocop:enable Lint/UselessAssignment

    erb = RSpecKickstarter::ERBFactory.new(@delta_template).get_instance_for_appending(rails_mode, spec_path)
    additional_spec = erb.result(binding)

    last_end_not_found = true
    code = existing_spec.split("\n").reverse.reject { |line|
      before_modified = last_end_not_found
      last_end_not_found = line.gsub(/#.+$/, '').strip != 'end' if before_modified
      before_modified
    }.reverse.join("\n") + "\n" + additional_spec + "\nend\n"

    if dry_run
      puts "----- #{spec_path} -----"
      puts code
    else
      File.open(spec_path, 'w') { |f| f.write(code) }
    end
    puts "#{spec_path} modified."
  end
end

#create_new_spec(class_or_module, dry_run, rails_mode, file_path, spec_path) ⇒ Object

Creates new spec.

rubocop:disable Metrics/AbcSize



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rspec_kickstarter/generator.rb', line 104

def create_new_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
  # These names are used in ERB template, don't delete.
  # rubocop:disable Lint/UselessAssignment
  methods_to_generate = class_or_module.method_list.select { |m| m.visibility == :public }
  c = class_or_module
  self_path = to_string_value_to_require(file_path)
  # rubocop:enable Lint/UselessAssignment

  erb = RSpecKickstarter::ERBFactory.new(@full_template).get_instance_for_new_spec(rails_mode, file_path)
  code = erb.result(binding)

  if dry_run
    puts "----- #{spec_path} -----"
    puts code
  else
    if File.exist?(spec_path)
      puts "#{spec_path} already exists."
    else
      FileUtils.mkdir_p(File.dirname(spec_path))
      File.open(spec_path, 'w') { |f| f.write(code) }
      puts "#{spec_path} created."
    end
  end
end

#get_block_code(method) ⇒ Object

e.g. { |a, b| }



224
225
226
227
228
229
230
# File 'lib/rspec_kickstarter/generator.rb', line 224

def get_block_code(method)
  if method.block_params.nil? || method.block_params.empty?
    ''
  else
    " { |#{method.block_params}| }"
  end
end

#get_complete_class_name(class_or_module, name = class_or_module.name) ⇒ Object

Gets the complete class name from RDoc::NormalClass/RDoc::NormalModule instance.



44
45
46
47
48
49
50
# File 'lib/rspec_kickstarter/generator.rb', line 44

def get_complete_class_name(class_or_module, name = class_or_module.name)
  if !class_or_module.parent.name.nil? && class_or_module.parent.is_a?(RDoc::NormalModule)
    get_complete_class_name(class_or_module.parent, "#{class_or_module.parent.name}::#{name}")
  else
    name
  end
end

#get_instantiation_code(c, method) ⇒ Object

e.g.

a = double('a')
b = double('b')
bar_baz = BarBaz.new(a, b)


182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rspec_kickstarter/generator.rb', line 182

def get_instantiation_code(c, method)
  if method.singleton
    ''
  else
    constructor = c.method_list.find { |m| m.name == 'new' }
    if constructor.nil?
      "      #{instance_name(c)} = #{get_complete_class_name(c)}.new\n"
    else
      get_params_initialization_code(constructor) +
        "      #{instance_name(c)} = #{get_complete_class_name(c)}.new#{to_params_part(constructor.params)}\n"
    end
  end
end

#get_method_invocation_code(c, method) ⇒ Object

e.g. BarBaz.do_something(a, b) { |c| }



209
210
211
212
# File 'lib/rspec_kickstarter/generator.rb', line 209

def get_method_invocation_code(c, method)
  target = method.singleton ? get_complete_class_name(c) : instance_name(c)
  "#{target}.#{method.name}#{to_params_part(method.params)}#{get_block_code(method)}"
end

#get_params_initialization_code(method) ⇒ Object

e.g.

a = double('a')
b = double('b')


201
202
203
204
# File 'lib/rspec_kickstarter/generator.rb', line 201

def get_params_initialization_code(method)
  code = to_param_names_array(method.params).map { |p| "      #{p} = double('#{p}')" }.join("\n")
  code.empty? ? '' : "#{code}\n"
end

#get_rails_helper_method_invocation_code(method) ⇒ Object

e.g. do_something(a, b) { |c| }



217
218
219
# File 'lib/rspec_kickstarter/generator.rb', line 217

def get_rails_helper_method_invocation_code(method)
  "#{method.name}#{to_params_part(method.params)}#{get_block_code(method)}"
end

#get_rails_http_method(method_name) ⇒ Object



232
233
234
235
# File 'lib/rspec_kickstarter/generator.rb', line 232

def get_rails_http_method(method_name)
  http_method = RAILS_RESOURCE_METHOD_AND_HTTP_METHOD[method_name]
  http_method.nil? ? 'get' : http_method
end

#get_spec_path(file_path) ⇒ Object

Returns spec file path. e.g. “lib/foo/bar_baz.rb” -> “spec/foo/bar_baz_spec.rb”



56
57
58
# File 'lib/rspec_kickstarter/generator.rb', line 56

def get_spec_path(file_path)
  spec_dir + '/' + file_path.gsub(/^\.\//, '').gsub(%r{^(lib/)|(app/)}, '').gsub(/\.rb$/, '_spec.rb')
end

#instance_name(c) ⇒ Object

Returns snake_case name. e.g. FooBar -> “foo_bar”



72
73
74
75
76
77
78
79
# File 'lib/rspec_kickstarter/generator.rb', line 72

def instance_name(c)
  c.name.
    gsub(/::/, '/').
    gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
    gsub(/([a-z\d])([A-Z])/, '\1_\2').
    tr('-', '_').
    downcase
end

#to_param_names_array(params) ⇒ Object

Extracts parameter names as an Array. e.g. “()” -> [] e.g. “(a, b = ‘foo’)” -> [“a”, “b”]



86
87
88
# File 'lib/rspec_kickstarter/generator.rb', line 86

def to_param_names_array(params)
  params.split(',').map { |p| p.gsub(/[\(\)\s]/, '').gsub(/=.+$/, '') }.reject { |p| p.nil? || p.empty? }
end

#to_params_part(params) ⇒ Object

Returns params part e.g. [“a”,“b”] -> “(a, b)” e.g. [] -> “”



95
96
97
98
# File 'lib/rspec_kickstarter/generator.rb', line 95

def to_params_part(params)
  param_csv = to_param_names_array(params).join(', ')
  param_csv.empty? ? '' : "(#{param_csv})"
end

#to_string_value_to_require(file_path) ⇒ Object

Returns string value to require. e.g. “lib/foo/bar_baz.rb” -> “foo/bar_baz”



64
65
66
# File 'lib/rspec_kickstarter/generator.rb', line 64

def to_string_value_to_require(file_path)
  file_path.gsub(%r{^(lib/)|(app/)}, '').gsub(/\.rb$/, '')
end

#write_spec(file_path, force_write = false, dry_run = false, rails_mode = false) ⇒ Object

Writes new spec or appends to the existing spec.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/rspec_kickstarter/generator.rb', line 27

def write_spec(file_path, force_write = false, dry_run = false, rails_mode = false)
  class_or_module = RSpecKickstarter::RDocFactory.get_rdoc_class_or_module(file_path)
  if class_or_module
    spec_path = get_spec_path(file_path)
    if force_write && File.exist?(spec_path)
      append_to_existing_spec(class_or_module, dry_run, rails_mode, spec_path)
    else
      create_new_spec(class_or_module, dry_run, rails_mode, file_path, spec_path)
    end
  else
    puts "#{file_path} skipped (Class/Module not found)."
  end
end