quick_shoulda 0.0.2
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/.gitignore +18 -0
- data/.rdebugrc +3 -0
- data/.rspec +4 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/data/stored_strings.yml +44 -0
- data/lib/generators/quick_shoulda/generate/generate_generator.rb +11 -0
- data/lib/quick_shoulda/errors.rb +33 -0
- data/lib/quick_shoulda/file_writer.rb +58 -0
- data/lib/quick_shoulda/generator/association.rb +46 -0
- data/lib/quick_shoulda/generator/spec_content.rb +31 -0
- data/lib/quick_shoulda/generator/validation.rb +144 -0
- data/lib/quick_shoulda/generator.rb +11 -0
- data/lib/quick_shoulda/helpers/string_helpers.rb +18 -0
- data/lib/quick_shoulda/main.rb +39 -0
- data/lib/quick_shoulda/path_resolver.rb +31 -0
- data/lib/quick_shoulda/random_string.rb +51 -0
- data/lib/quick_shoulda/version.rb +3 -0
- data/lib/quick_shoulda.rb +26 -0
- data/quick_shoulda.gemspec +23 -0
- data/spec/file_writer_spec.rb +109 -0
- data/spec/fixtures/user_spec.rb +11 -0
- data/spec/fixtures/write_spec.rb +3 -0
- data/spec/generator/association_spec.rb +51 -0
- data/spec/generator/spec_content_spec.rb +65 -0
- data/spec/generator/validation_spec.rb +407 -0
- data/spec/main_spec.rb +154 -0
- data/spec/models/not_a_ruby_file.txt +0 -0
- data/spec/path_resolver_spec.rb +88 -0
- data/spec/quick_shoulda_spec.rb +64 -0
- data/spec/random_string_spec.rb +93 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/string_helpers_spec.rb +34 -0
- metadata +127 -0
data/.gitignore
ADDED
data/.rdebugrc
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rvm use 1.9.3-p194@quick-shoulda --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Tien Nguyen
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# QuickShoulda
|
|
2
|
+
|
|
3
|
+
TODO: Write a gem description
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'quick_shoulda'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install quick_shoulda
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
TODO: Write usage instructions here
|
|
22
|
+
|
|
23
|
+
## Contributing
|
|
24
|
+
|
|
25
|
+
1. Fork it
|
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[
|
|
2
|
+
#URLs
|
|
3
|
+
'www.domain.com', 'www.domain.subdomain.com', 'http://www.domain.com', 'http://www.domain.subdomain.com',
|
|
4
|
+
'http://www.domain.com/path/1', 'https://www.domain.com', 'https://www.domain.subdomain.com', 'domain.subdomain.com',
|
|
5
|
+
'domain.com/path/1', 'subdomain.domain.com/path/1',
|
|
6
|
+
#Emails
|
|
7
|
+
'us@gmail.com', 'us.vn@gmail.com', 'us_vn@gmail.com', 'us_vn@gmail.com.vn',
|
|
8
|
+
#IPS
|
|
9
|
+
'192.168.1.1', '192.168.1.2',
|
|
10
|
+
#Sex
|
|
11
|
+
'male', 'female', 'gay', 'les', 'other',
|
|
12
|
+
#Zip postal code
|
|
13
|
+
'10001', '10002', '10003',
|
|
14
|
+
#Phone numbers
|
|
15
|
+
'732-757-2923', '1732-757-2923',
|
|
16
|
+
#datestimes
|
|
17
|
+
'1999-01-01', '99-01-01', '1999/01/01', '99/01/01', '01/01/99', '03/10/1986', '01-01-1999', '01-01-99', 'January 07, 2013',
|
|
18
|
+
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday',
|
|
19
|
+
'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december',
|
|
20
|
+
'am', 'pm',
|
|
21
|
+
#countries
|
|
22
|
+
'Vietnam', 'America', 'England',
|
|
23
|
+
'Vietnamese', 'American', 'English',
|
|
24
|
+
#cities
|
|
25
|
+
'New York', 'London', 'Ho Chi Minh',
|
|
26
|
+
#Status
|
|
27
|
+
'married', 'single', 'available', 'complicated',
|
|
28
|
+
#Files
|
|
29
|
+
'filename.txt', 'filename.jpg', 'filename.png', 'filename.jpeg',
|
|
30
|
+
#Extensions
|
|
31
|
+
'txt', 'jpg', 'png', 'jpeg', 'mp4', 'mp3', 'wmv',
|
|
32
|
+
#Videos
|
|
33
|
+
'filename.wmv', 'filename.mp4', 'filename.flv',
|
|
34
|
+
#credit cards number
|
|
35
|
+
'4111 1111 1111 1111', '5500 0000 0000 0004', '3400 0000 0000 009', '3000 0000 0000 04', '+84 121731845', '84 121731845', '(84) 121731845',
|
|
36
|
+
#some countries code
|
|
37
|
+
'84', '1', '39',
|
|
38
|
+
#ISO countries code
|
|
39
|
+
'US', 'USA', 'VN', 'VNM',
|
|
40
|
+
#services'
|
|
41
|
+
'yahoo', 'google', 'facebook', 'amazone', 'youtube', 'paypal', 'alchemy',
|
|
42
|
+
#marks
|
|
43
|
+
'excellent', 'good', 'bad'
|
|
44
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
module Errors
|
|
3
|
+
class InvalidPathError < StandardError
|
|
4
|
+
def initialize(msg = 'Invalid path given')
|
|
5
|
+
super(msg)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class FileDoesNotExistError < StandardError
|
|
10
|
+
def initialize(msg = 'This file does not exist')
|
|
11
|
+
super(msg)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class NotAModelPathError < StandardError
|
|
16
|
+
def initialize(msg = 'file must located inside models directory')
|
|
17
|
+
super(msg)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class NotRubyFileError < StandardError
|
|
22
|
+
def initialize(msg = 'Given file must have .rb extension')
|
|
23
|
+
super(msg)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class PathNotGivenError < StandardError
|
|
28
|
+
def initialize(msg = 'path to model file must be given')
|
|
29
|
+
super(msg)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'quick_shoulda/errors'
|
|
2
|
+
|
|
3
|
+
module QuickShoulda
|
|
4
|
+
module FileWriter
|
|
5
|
+
DescribeRegex = /^describe\s.+\sdo$/i
|
|
6
|
+
|
|
7
|
+
def create_file_and_write_init_content
|
|
8
|
+
FileUtils.mkdir_p(File.dirname(spec_file_path))
|
|
9
|
+
File.open(spec_file_path, 'w') do |file|
|
|
10
|
+
file.write(spec_init_content)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def clear_all_blocks
|
|
15
|
+
[:validation, :association].each do |block_name|
|
|
16
|
+
clear_block block_describe_header(block_name)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write_block(shoulda_content)
|
|
21
|
+
file_content = ''
|
|
22
|
+
File.open(spec_file_path, 'a+') do |file|
|
|
23
|
+
inserted = false
|
|
24
|
+
while( line = file.gets )
|
|
25
|
+
file_content << line
|
|
26
|
+
if !inserted && line.strip.gsub(/[\t\n]/,'') =~ DescribeRegex
|
|
27
|
+
file_content << shoulda_content
|
|
28
|
+
inserted = true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
File.open(spec_file_path, 'w') { |file | file.write( file_content ) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def clear_block(block)
|
|
37
|
+
file_content = ""
|
|
38
|
+
File.open(spec_file_path, 'r') do | file |
|
|
39
|
+
|
|
40
|
+
delete_mode = false
|
|
41
|
+
|
|
42
|
+
while( line = file.gets )
|
|
43
|
+
filtered_line = line.gsub(/[\n\t]/,'')
|
|
44
|
+
|
|
45
|
+
if filtered_line == block || delete_mode
|
|
46
|
+
delete_mode = true if filtered_line == block
|
|
47
|
+
delete_mode = false if filtered_line == 'end'
|
|
48
|
+
next
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
file_content << line
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
File.open(spec_file_path, 'w') { |file | file.write( file_content ) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
module Generator
|
|
3
|
+
module Association
|
|
4
|
+
MappedMacros = {
|
|
5
|
+
:has_one => 'have_one',
|
|
6
|
+
:has_many => 'have_many',
|
|
7
|
+
:belongs_to => 'belong_to',
|
|
8
|
+
:has_and_belongs_to_many => 'have_and_belong_to_many'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
OptionMethods = {
|
|
12
|
+
:dependent => 'dependent',
|
|
13
|
+
:through => 'through',
|
|
14
|
+
:class_name => 'class_name',
|
|
15
|
+
:touch => 'touch',
|
|
16
|
+
:validate => 'validate',
|
|
17
|
+
:order => 'order',
|
|
18
|
+
:foreign_key => 'with_foreign_key'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
SingleQuoteMethods = ['order', 'class_name']
|
|
22
|
+
|
|
23
|
+
def generate_associations(model)
|
|
24
|
+
model.reflect_on_all_associations.map { |association| generate_for_association(association) }.compact.flatten
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def generate_for_association(association)
|
|
30
|
+
shoulda_method_name = MappedMacros[association.macro.to_sym]
|
|
31
|
+
"it { should #{shoulda_method_name}(:#{association.name})#{shoulda_assciation_option_methods_chain(association.options)} }"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def shoulda_assciation_option_methods_chain(options)
|
|
35
|
+
options.map do |option, value|
|
|
36
|
+
shoulda_option_method_with_value(option, value)
|
|
37
|
+
end.compact.join
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def shoulda_option_method_with_value(option, value)
|
|
41
|
+
method_name = OptionMethods[option.to_sym]
|
|
42
|
+
".#{method_name}#{value_transform(value)}" if method_name
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
module Generator
|
|
3
|
+
module SpecContent
|
|
4
|
+
def block_describe_header(block_name)
|
|
5
|
+
case block_name.to_sym
|
|
6
|
+
when :association
|
|
7
|
+
"describe '#Associations' do"
|
|
8
|
+
when :validation
|
|
9
|
+
"describe '#Validations' do"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def shoulda_content(block_name, shoulda_lines)
|
|
14
|
+
return "" unless shoulda_lines.size > 0
|
|
15
|
+
|
|
16
|
+
block = block_describe_header(block_name)
|
|
17
|
+
shoulda_lines.map! { |line| "\t#{line}"}
|
|
18
|
+
shoulda_lines.insert(0, "#{block}")
|
|
19
|
+
shoulda_lines << "end\n"
|
|
20
|
+
|
|
21
|
+
shoulda_lines.map { |line| "\t#{line}"}.join("\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def spec_init_content
|
|
25
|
+
content = "require 'spec_helper'\n\n"
|
|
26
|
+
content << "describe '#{model_full_namespace}' do\n"
|
|
27
|
+
content << "end"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require 'quick_shoulda/random_string'
|
|
2
|
+
|
|
3
|
+
module QuickShoulda
|
|
4
|
+
module Generator
|
|
5
|
+
module Validation
|
|
6
|
+
AllowsValidators = [
|
|
7
|
+
'presence', 'uniqueness', 'acceptance', 'confirmation', 'exclusion', 'format', 'inclusion', 'length', 'numericality'
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
InclusionOptions = {
|
|
11
|
+
:range => 'in_range',
|
|
12
|
+
:array => 'in_array'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
OptionMethods = {
|
|
16
|
+
:minimum => 'is_at_least',
|
|
17
|
+
:maximum => 'is_at_most',
|
|
18
|
+
:is => 'is_equal_to',
|
|
19
|
+
:scope => 'scoped_to',
|
|
20
|
+
:too_short => 'with_short_message',
|
|
21
|
+
:too_long => 'with_long_message',
|
|
22
|
+
:message => 'with_message',
|
|
23
|
+
:only_integer => 'only_integer',
|
|
24
|
+
:case_insensitive => 'case_insensitive',
|
|
25
|
+
:allow_nil => 'allow_nil',
|
|
26
|
+
:allow_blank => 'allow_blank',
|
|
27
|
+
:in => InclusionOptions,
|
|
28
|
+
:within => InclusionOptions
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
Filters = {
|
|
32
|
+
:allow_blank => ['inclusion'],
|
|
33
|
+
:allow_nil => ['inclusion', 'uniqueness'],
|
|
34
|
+
:case_insensitive => ['uniqueness']
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
AttrsFilters = ['friendly_id']
|
|
38
|
+
|
|
39
|
+
def self.included(base)
|
|
40
|
+
base.class_eval do
|
|
41
|
+
attr_accessor :validation_type
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def generate_validations(model)
|
|
46
|
+
model.validators.map { |validator| generate_for_validator(validator) }.compact.flatten
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def generate_for_validator(validator)
|
|
52
|
+
if @validation_type = _validation_type(validator)
|
|
53
|
+
return if ( attrs = attrs_filter(validator.attributes) ).empty?
|
|
54
|
+
generate_shouldas(attrs, validator.options)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def attrs_filter(attrs)
|
|
59
|
+
attrs.select { |attr| !(AttrsFilters.include? attr.to_s) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def _validation_type(validator)
|
|
63
|
+
validation_type = validator.class.to_s.scan(/([^:]+)Validator/)
|
|
64
|
+
unless validation_type.empty?
|
|
65
|
+
validator = validation_type.first.first.downcase
|
|
66
|
+
validator if AllowsValidators.include? validator
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def generate_shouldas(attributes, options)
|
|
71
|
+
attributes.map { |attribute| generate_shoulda(attribute, options) }.flatten
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def generate_shoulda(attribute, options)
|
|
75
|
+
unless validation_type == 'format'
|
|
76
|
+
[ "it { should #{shoulda_matcher_method}(:#{attribute})#{shoulda_option_methods_chain(options)} }" ]
|
|
77
|
+
else
|
|
78
|
+
generate_shouldas_for_format_validation(attribute, options)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def generate_shouldas_for_format_validation(attribute, options)
|
|
83
|
+
RandomString.generate(options[:with]).map { |type, strings| generate_allow_shouldas(type, strings, attribute) }.flatten
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def generate_allow_shouldas(type, strings, attribute)
|
|
87
|
+
should_suffix = type.to_s == 'matched_strings' ? '' : '_not'
|
|
88
|
+
strings.map { |string| "it { should#{should_suffix} allow_value('#{string}').for(:#{attribute}) }" }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def shoulda_matcher_method
|
|
92
|
+
prefix = ('exclusion|inclusion|length' =~ /#{validation_type}/) ? 'ensure' : 'validate'
|
|
93
|
+
"#{prefix}_#{validation_type}_of"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#.is_at_least(50).is_at_max(100)
|
|
97
|
+
def shoulda_option_methods_chain(options)
|
|
98
|
+
options.map { |option, value| shoulda_option_method(option, value) }.compact.join
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def shoulda_option_method(option, value)
|
|
102
|
+
method = shoulda_option_method_name(option, value)
|
|
103
|
+
return unless method
|
|
104
|
+
|
|
105
|
+
case method
|
|
106
|
+
when OptionMethods[:scope]
|
|
107
|
+
shoulda_scope_to_method(value)
|
|
108
|
+
when OptionMethods[:only_integer], OptionMethods[:case_insensitive]
|
|
109
|
+
shoulda_method_without_value(method)
|
|
110
|
+
else
|
|
111
|
+
shoulda_normal_option_method(method, value)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#.is_at_least
|
|
116
|
+
#.is_at_most
|
|
117
|
+
def shoulda_option_method_name(option, value)
|
|
118
|
+
return unless pass_filter?(option)
|
|
119
|
+
option_method = OptionMethods[option.to_sym]
|
|
120
|
+
option_method = option_method[value.class.to_s.downcase.to_sym] if option_method.is_a?(Hash)
|
|
121
|
+
option_method
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def pass_filter?(option)
|
|
125
|
+
!Filters[option.to_sym] || Filters[option.to_sym].include?(validation_type)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#.is_at_least(50)
|
|
129
|
+
def shoulda_normal_option_method(method, value)
|
|
130
|
+
".#{method}#{value_transform(value)}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def shoulda_method_without_value(method)
|
|
134
|
+
".#{method}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
#.scoped_to(:username).scoped_to(:account)
|
|
138
|
+
def shoulda_scope_to_method(value)
|
|
139
|
+
method = OptionMethods[:scope]
|
|
140
|
+
([] << value).flatten.map { | v | ".#{method}#{value_transform(v)}" }.join
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'quick_shoulda/generator/validation'
|
|
2
|
+
require 'quick_shoulda/generator/association'
|
|
3
|
+
require 'quick_shoulda/generator/spec_content'
|
|
4
|
+
|
|
5
|
+
module QuickShoulda
|
|
6
|
+
module Generator
|
|
7
|
+
include Validation
|
|
8
|
+
include Association
|
|
9
|
+
include SpecContent
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
module StringHelpers
|
|
3
|
+
def camelize(str)
|
|
4
|
+
return str if str !~ /_/ && str =~ /[A-Z]+.*/
|
|
5
|
+
str.split("_").map{|e| e.capitalize}.join
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def value_transform(value)
|
|
9
|
+
if value.is_a? String
|
|
10
|
+
"('#{value}')"
|
|
11
|
+
elsif value.is_a? Symbol
|
|
12
|
+
"(:#{value})"
|
|
13
|
+
else
|
|
14
|
+
"(#{value})"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
module Main
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.class_eval do
|
|
5
|
+
attr_accessor :path, :spec_folder, :model_full_namespace, :spec_file_path, :validations_block, :associations_block
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def configure_and_generate(path, spec_folder)
|
|
10
|
+
configure(path, spec_folder)
|
|
11
|
+
generate
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def configure(path, spec_folder)
|
|
17
|
+
@path = path
|
|
18
|
+
@spec_folder = spec_folder[-1] != '/' ? "#{spec_folder}/" : spec_folder
|
|
19
|
+
@model_full_namespace = _model_full_namespace
|
|
20
|
+
@spec_file_path = _spec_file_path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def generate
|
|
24
|
+
generate_shoulda_content
|
|
25
|
+
create_and_write_to_file
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def generate_shoulda_content
|
|
29
|
+
@validations_block = shoulda_content(:validation, generate_validations(model_full_namespace))
|
|
30
|
+
@associations_block = shoulda_content(:association, generate_associations(model_full_namespace))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def create_and_write_to_file
|
|
34
|
+
create_file_and_write_init_content unless spec_file_exist?
|
|
35
|
+
clear_all_blocks
|
|
36
|
+
write_block(validations_block + associations_block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'quick_shoulda/helpers/string_helpers'
|
|
2
|
+
|
|
3
|
+
module QuickShoulda
|
|
4
|
+
module PathResolver
|
|
5
|
+
include StringHelpers
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def spec_file_exist?
|
|
10
|
+
File.file? spec_file_path
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def _model_full_namespace
|
|
14
|
+
eval "::#{_model_full_namespace_in_str}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def _spec_file_path
|
|
18
|
+
model_path = path.split(/\/?models\//)[1]
|
|
19
|
+
file_name = "#{File.basename(model_path, ".rb")}_spec.rb"
|
|
20
|
+
dir_name = File.dirname(model_path) == '.' ? spec_folder : "#{spec_folder}#{File.dirname(model_path)}/"
|
|
21
|
+
"#{dir_name}#{file_name}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# given models/namespace/modelname.rb
|
|
25
|
+
# return Namespace::ModelName
|
|
26
|
+
def _model_full_namespace_in_str
|
|
27
|
+
model_path = path.split(/\/?models\//)[1].gsub('.rb','')
|
|
28
|
+
model_path.split('/').map { |token| camelize(token) }.join('::')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module QuickShoulda
|
|
2
|
+
class RandomString
|
|
3
|
+
class << self
|
|
4
|
+
Size = 3
|
|
5
|
+
LowerCaseChars = ('a'..'z').to_a
|
|
6
|
+
UpperCaseChars = ('A'..'Z').to_a
|
|
7
|
+
Digits = (0..9).to_a
|
|
8
|
+
|
|
9
|
+
def sample_strings
|
|
10
|
+
stored_strings + random_digits_strings + random_chars_strings
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def stored_strings
|
|
14
|
+
@stored_strings ||= YAML.load File.new(File.join(File.dirname(__FILE__),'..','..','data','stored_strings.yml'))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def random_digits_strings
|
|
18
|
+
random_strings(Digits)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def random_chars_strings
|
|
22
|
+
random_strings(LowerCaseChars + UpperCaseChars)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def random_strings(base)
|
|
26
|
+
(1..50).map { |length| length.times.inject('') { |initial, n| initial + base[rand(10)].to_s } }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def generate(pattern)
|
|
30
|
+
matched_strings = []
|
|
31
|
+
unmatched_strings = []
|
|
32
|
+
RandomString.sample_strings.each do |sample_string|
|
|
33
|
+
break if matched_strings.size == Size && unmatched_strings.size == Size
|
|
34
|
+
|
|
35
|
+
if sample_string =~ pattern
|
|
36
|
+
matched_strings << sample_string if matched_strings.size < Size
|
|
37
|
+
else
|
|
38
|
+
unmatched_strings << sample_string if unmatched_strings.size < Size
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
matched_strings: matched_strings,
|
|
44
|
+
unmatched_strings: unmatched_strings
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
alias_method :gen, :generate
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "quick_shoulda/version"
|
|
2
|
+
require "quick_shoulda/errors"
|
|
3
|
+
require "quick_shoulda/path_resolver"
|
|
4
|
+
require "quick_shoulda/file_writer"
|
|
5
|
+
require "quick_shoulda/generator"
|
|
6
|
+
require "quick_shoulda/main"
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
|
|
9
|
+
module QuickShoulda
|
|
10
|
+
class << self
|
|
11
|
+
include StringHelpers
|
|
12
|
+
include PathResolver
|
|
13
|
+
include FileWriter
|
|
14
|
+
include Generator
|
|
15
|
+
include Main
|
|
16
|
+
include Errors
|
|
17
|
+
|
|
18
|
+
def process(path, spec_folder = 'spec/models/')
|
|
19
|
+
raise PathNotGivenError unless path
|
|
20
|
+
raise NotAModelPathError unless path =~ /models\//
|
|
21
|
+
raise FileDoesNotExistError unless File.file?(path)
|
|
22
|
+
raise NotRubyFileError unless File.extname(path) == '.rb'
|
|
23
|
+
configure_and_generate(path, spec_folder)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|