rails_zen 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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +102 -0
- data/Rakefile +8 -0
- data/bin/rails_zen +4 -0
- data/features/model/act.feature +49 -0
- data/features/model/g.feature +64 -0
- data/features/step_definitions/mock_file_steps.rb +18 -0
- data/features/support/env.rb +9 -0
- data/lib/cli.rb +10 -0
- data/lib/cli/model.rb +33 -0
- data/lib/rails_zen.rb +5 -0
- data/lib/rails_zen/chosen_attr.rb +89 -0
- data/lib/rails_zen/given_model_gen.rb +76 -0
- data/lib/rails_zen/model_action.rb +106 -0
- data/lib/rails_zen/version.rb +3 -0
- data/lib/rails_zen/write_to_files.rb +31 -0
- data/lib/rails_zen/write_to_files/model_level_validation.rb +12 -0
- data/lib/rails_zen/write_to_files/model_level_validation_spec.rb +14 -0
- data/lib/rails_zen/write_to_files/write_to_migration.rb +37 -0
- data/lib/rails_zen/write_to_files/write_to_model.rb +57 -0
- data/lib/rails_zen/write_to_files/write_to_spec.rb +45 -0
- data/lib/railtie.rb +8 -0
- data/rails_zen.gemspec +29 -0
- data/spec/chosen_attr_spec.rb +92 -0
- data/spec/given_model_gen_spec.rb +81 -0
- data/spec/model_action_spec.rb +81 -0
- data/spec/shared/build_file_attr.rb +12 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/support/aruba.rb +11 -0
- data/spec/write_to_files/write_to_migration_spec.rb +50 -0
- data/spec/write_to_files/write_to_model_spec.rb +57 -0
- data/spec/write_to_files/write_to_spec_spec.rb +63 -0
- data/spec/write_to_files_spec.rb +9 -0
- metadata +196 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rails_zen/chosen_attr'
|
2
|
+
require 'rails_zen/write_to_files'
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
module RailsZen
|
6
|
+
class GivenModelGen
|
7
|
+
attr_accessor :name, :raw_attributes, :simple_attributes
|
8
|
+
|
9
|
+
def initialize(name, raw_attrs)
|
10
|
+
@name = name
|
11
|
+
@raw_attributes = raw_attrs
|
12
|
+
@simple_attributes = []
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def step_by_step
|
17
|
+
say "\nThese are your attributes"
|
18
|
+
say "---------------------------\n"
|
19
|
+
|
20
|
+
attrs.each_with_index { |attr, i| puts "#{i} #{attr}" }
|
21
|
+
say "\n\nChoose the one that don't require 'presence true' or 'validations' or uniqueness.\n Enter like this eg: 0,1. or just enter "
|
22
|
+
say "\n----------------------------------\n\n$.> "
|
23
|
+
|
24
|
+
@simple_attributes = ask("Enter (comma sep list) ", lambda { |str| str.split(/,\s*/) })
|
25
|
+
|
26
|
+
chosen_attrs.each do |attr_obj|
|
27
|
+
attr_obj.get_user_inputs
|
28
|
+
RailsZen::WriteToFiles.new(attr_obj, name).write
|
29
|
+
end
|
30
|
+
|
31
|
+
ask_for_has_many_relations
|
32
|
+
end
|
33
|
+
|
34
|
+
def chosen_attrs
|
35
|
+
|
36
|
+
attr_hash = attrs_with_types
|
37
|
+
final_attr_objs = []
|
38
|
+
i = -1
|
39
|
+
attr_hash.each do |attr, type|
|
40
|
+
i += 1
|
41
|
+
unless simple_attributes.include? "#{i}"
|
42
|
+
final_attr_objs << RailsZen::ChosenAttr.new(attr, type)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
final_attr_objs
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def ask_for_has_many_relations
|
50
|
+
say "\nDo you have any has_many relations? Enter the name if so, otherwise leave it. eg: posts,comments"
|
51
|
+
|
52
|
+
@has_many_relations = ask("Enter (comma sep list) ", lambda { |str| str.split(/,\s*/) })
|
53
|
+
|
54
|
+
@has_many_relations.each do |rel|
|
55
|
+
|
56
|
+
m = RailsZen::WriteToModel.new
|
57
|
+
m.model_name = @name
|
58
|
+
m.adding_to_file!(" has_many :#{rel}\n")
|
59
|
+
|
60
|
+
s = RailsZen::WriteToSpec.new
|
61
|
+
s.model_name = @name
|
62
|
+
s.adding_to_file!("it { is_expected.to have_many(:#{rel}) }")
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
def attrs
|
67
|
+
raw_attributes.scan(/\w+(?=:)/)
|
68
|
+
end
|
69
|
+
private
|
70
|
+
def attrs_with_types
|
71
|
+
raw_attributes.scan(/(\w+):(\w+)/).to_h
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'rails_zen/write_to_files/write_to_model'
|
2
|
+
require 'rails_zen/write_to_files/write_to_spec'
|
3
|
+
require 'highline/import'
|
4
|
+
|
5
|
+
module RailsZen
|
6
|
+
class ModelAction
|
7
|
+
attr_writer :is_class_action
|
8
|
+
|
9
|
+
def initialize(name, is_class_action, model)
|
10
|
+
@name = name
|
11
|
+
@model = model
|
12
|
+
@is_class_action = is_class_action
|
13
|
+
@arg_names = []
|
14
|
+
@args = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def write!
|
18
|
+
get_necessary_info
|
19
|
+
|
20
|
+
m = RailsZen::WriteToModel.new
|
21
|
+
m.model_name = @model
|
22
|
+
m.adding_to_file!(action_string)
|
23
|
+
|
24
|
+
s = RailsZen::WriteToSpec.new
|
25
|
+
s.model_name = @model
|
26
|
+
|
27
|
+
unless File.foreach("spec/models/#{@model}_spec.rb").grep(factory_girl_match).any? #check for factory
|
28
|
+
s.adding_to_file!(factory_method)
|
29
|
+
end
|
30
|
+
s.adding_to_file!(action_spec_string)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def factory_girl_match
|
36
|
+
Regexp.new("FactoryGirl.create(:#{@model})")
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_necessary_info
|
40
|
+
say "What does your method do? (Please don't use 'it' or 'should' in your definition)\n"
|
41
|
+
@functionality = ask("Eg: returns sum of 2 numbers\n")
|
42
|
+
|
43
|
+
say "\n\n------------ARGUMENTS-----------------\n"
|
44
|
+
|
45
|
+
say "\nWhat name would you give your arguments for the #{@name} method? eg: num1,num2\n"
|
46
|
+
|
47
|
+
@arg_names = ask("Enter (comma sep) list", lambda { |str| str.split(/,\s*/) })
|
48
|
+
|
49
|
+
if @arg_names.any?
|
50
|
+
say "\nGive example arguments inputs\n"
|
51
|
+
@args = ask("Enter (comma sep list). Eg: 1,2 ", lambda { |str| str.split(/,\s*/) })
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
@expected = ask("Enter the expected output for the previously entered arguments. eg: 3")
|
56
|
+
end
|
57
|
+
|
58
|
+
def action_string
|
59
|
+
%{
|
60
|
+
def #{method_with_args}
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def action_spec_string
|
66
|
+
if @is_class_action
|
67
|
+
%{
|
68
|
+
it { is_expected.to respond_to :#{@name}}
|
69
|
+
|
70
|
+
describe ".#{@name}" do
|
71
|
+
it "#{@functionality}" do
|
72
|
+
expect(#{@model.capitalize}.#{@name}(#{sample_arguments})).to eq \"#{@expected}\"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
}
|
77
|
+
else
|
78
|
+
%{
|
79
|
+
describe "##{@name}" do
|
80
|
+
it "#{@functionality}" do
|
81
|
+
expect(#{@model}.#{@name}(#{sample_arguments})).to eq \"#{@expected}\"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
def method_with_args
|
89
|
+
if @is_class_action
|
90
|
+
"self.#{@name}(#{arguments})"
|
91
|
+
else
|
92
|
+
"#{@name}(#{arguments})"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def sample_arguments
|
97
|
+
@args.join(", ")
|
98
|
+
end
|
99
|
+
def arguments
|
100
|
+
@arg_names.join(", ")
|
101
|
+
end
|
102
|
+
def factory_method
|
103
|
+
"let(:#{@model}) { FactoryGirl.create(:#{@model}) }"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails_zen/write_to_files/write_to_spec'
|
2
|
+
require 'rails_zen/write_to_files/write_to_model'
|
3
|
+
require 'rails_zen/write_to_files/write_to_migration'
|
4
|
+
|
5
|
+
class RailsZen::WriteToFiles
|
6
|
+
attr_reader :attr, :model_name
|
7
|
+
|
8
|
+
def initialize(attr, model_name)
|
9
|
+
@attr = attr
|
10
|
+
@model_name = model_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_file_writers(type)
|
14
|
+
# When including thor, could not use initialize hence building it
|
15
|
+
Object.const_get("RailsZen::#{type}").new.tap do |w|
|
16
|
+
w.name = attr.name
|
17
|
+
w.attr_type = attr.type
|
18
|
+
w.scope_attr = attr.scope_attr
|
19
|
+
w.validator = attr.validator
|
20
|
+
w.type_based_validators = attr.type_based_validators
|
21
|
+
w.model_name = model_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def write
|
26
|
+
["WriteToSpec", "WriteToMigration", "WriteToModel"].each do |type|
|
27
|
+
build_file_writers(type).write!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RailsZen::ModelLeveLValidationSpec
|
2
|
+
|
3
|
+
def validate_belongs_to
|
4
|
+
"it { is_expected.to belong_to(:#{name}) }"
|
5
|
+
end
|
6
|
+
def validate_numericality
|
7
|
+
"it { is_expected.to validates_numericality_of(:#{name}) }"
|
8
|
+
end
|
9
|
+
def validate_integer
|
10
|
+
"it { is_expected.to validates_numericality_of(:#{name}).only_integer }"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "rails_zen/write_to_files/write_to_model"
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
class RailsZen::WriteToMigration < RailsZen::WriteToModel
|
5
|
+
|
6
|
+
def write!
|
7
|
+
if @validator
|
8
|
+
line = send(@validator)
|
9
|
+
append_to_line(line)
|
10
|
+
end
|
11
|
+
if scope_attr.any?
|
12
|
+
inject_into_file file_name, "t.index #{scope_attr.to_s}\n", before: "t.timestamps"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
def append_to_line(line)
|
16
|
+
gsub_file file_name, /t.#{attr_type}.+#{name}.*$/ do |match|
|
17
|
+
match = line
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_name
|
22
|
+
Dir.glob("db/migrate/*create_#{@model_name.pluralize}.rb")[0]
|
23
|
+
# need to use pluralize here
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def validates_presence_of
|
28
|
+
"t.#{attr_type} :#{name}, required: true, null: false"
|
29
|
+
end
|
30
|
+
def validates_uniqueness_of
|
31
|
+
"t.#{attr_type} :#{name}, required: true, null: false, index: true"
|
32
|
+
end
|
33
|
+
def validates_uniqueness_scoped_to
|
34
|
+
"t.#{attr_type} :#{name}, required: true, null: false, index: true"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require "rails_zen/write_to_files/model_level_validation"
|
3
|
+
|
4
|
+
module RailsZen
|
5
|
+
class WriteToModel
|
6
|
+
|
7
|
+
|
8
|
+
attr_accessor :name, :scope_attr, :validator, :type_based_validators, :model_name, :attr_type, :file_name
|
9
|
+
include RailsZen::ModelLeveLValidation
|
10
|
+
include Thor::Base
|
11
|
+
include Thor::Actions
|
12
|
+
|
13
|
+
|
14
|
+
def write!
|
15
|
+
|
16
|
+
if @validator
|
17
|
+
output = send(@validator)
|
18
|
+
if type_num?
|
19
|
+
type_output = send(@type_based_validators)
|
20
|
+
adding_to_file!(" #{output + type_output}\n" )
|
21
|
+
else
|
22
|
+
adding_to_file!(" #{output}\n")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def adding_to_file!(line)
|
28
|
+
inject_into_class(file_name, model_name.capitalize, line)
|
29
|
+
end
|
30
|
+
|
31
|
+
def file_name
|
32
|
+
"app/models/#{model_name}.rb"
|
33
|
+
end
|
34
|
+
private
|
35
|
+
|
36
|
+
def type_num?
|
37
|
+
@attr_type == "integer" || @attr_type == "decimal"
|
38
|
+
end
|
39
|
+
def validates_presence_of
|
40
|
+
"validates :#{name}, presence: true"
|
41
|
+
end
|
42
|
+
def validates_uniqueness_of
|
43
|
+
"validates :#{name}, presence: true, uniqueness: true"
|
44
|
+
end
|
45
|
+
def validates_uniqueness_scoped_to
|
46
|
+
"validates_uniqueness_of :#{scope_attr[0]}, scope: #{resolve_scope}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def resolve_scope
|
50
|
+
if scope_attr.length == 2
|
51
|
+
":#{scope_attr[1]}"
|
52
|
+
else
|
53
|
+
scope_attr[1..-1].to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "rails_zen/write_to_files/model_level_validation_spec"
|
2
|
+
require "rails_zen/write_to_files/write_to_model"
|
3
|
+
|
4
|
+
class RailsZen::WriteToSpec < RailsZen::WriteToModel
|
5
|
+
|
6
|
+
include RailsZen::ModelLeveLValidationSpec
|
7
|
+
|
8
|
+
def write!
|
9
|
+
if @validator
|
10
|
+
unless File.foreach("spec/models/#{@model_name}_spec.rb").grep(factory_girl_match).any?
|
11
|
+
adding_to_file!(factory_method)
|
12
|
+
end
|
13
|
+
adding_to_file!(send(@validator))
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
adding_to_file!(send(@type_based_validator)) if @type_based_validator
|
18
|
+
end
|
19
|
+
def adding_to_file!(output)
|
20
|
+
inject_into_file(file_name, " #{output}\n", after: "RSpec.describe #{@model_name.capitalize}, type: :model do\n" )
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def factory_girl_match
|
25
|
+
Regexp.new("FactoryGirl.create(:#{@model_name})")
|
26
|
+
end
|
27
|
+
def factory_method
|
28
|
+
"let(:#{@model_name}) { FactoryGirl.create(:#{@model_name}) }"
|
29
|
+
end
|
30
|
+
|
31
|
+
def file_name
|
32
|
+
"spec/models/#{@model_name}_spec.rb"
|
33
|
+
end
|
34
|
+
|
35
|
+
def validates_presence_of
|
36
|
+
"it { #{@model_name}; is_expected.to validate_presence_of(:#{name})}"
|
37
|
+
end
|
38
|
+
def validates_uniqueness_of
|
39
|
+
"it { #{@model_name}; is_expected.to validate_uniqueness_of(:#{name})}"
|
40
|
+
end
|
41
|
+
def validates_uniqueness_scoped_to
|
42
|
+
"it { #{@model_name}; is_expected.to validate_uniqueness_of(:#{scope_attr[0]}).scoped_to(#{resolve_scope})}"
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/railtie.rb
ADDED
data/rails_zen.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails_zen/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rails_zen"
|
8
|
+
spec.version = RailsZen::VERSION
|
9
|
+
spec.authors = ["Vysakh Sreenivasan"]
|
10
|
+
spec.email = ["diplomatv@gmail.com"]
|
11
|
+
spec.summary = %q{Interactive rails generator generating files (models, migration) and related specs.}
|
12
|
+
spec.description = %q{Automate the writing of validations, relations in files and specs. Create boilerplate validation in models, specs, migrations. This specs are generated based on rspec, shoulda-matchers and factorygirl }
|
13
|
+
spec.homepage = "http://github.com/vysakh0/rails_zen"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'thor', "~> 0.19"
|
22
|
+
spec.add_dependency 'highline', "~> 1.4"
|
23
|
+
spec.add_dependency 'activesupport', "~> 4.2"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
26
|
+
spec.add_development_dependency "aruba", "~> 0.6"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.2"
|
29
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rails_zen/chosen_attr'
|
2
|
+
|
3
|
+
RSpec.describe RailsZen::ChosenAttr do
|
4
|
+
|
5
|
+
|
6
|
+
subject {
|
7
|
+
RailsZen::ChosenAttr.new("name", "string")
|
8
|
+
}
|
9
|
+
|
10
|
+
#after do
|
11
|
+
#$stdin = STDIN
|
12
|
+
#end
|
13
|
+
|
14
|
+
it { is_expected.to respond_to :get_user_inputs}
|
15
|
+
|
16
|
+
|
17
|
+
describe "#get_presence_req" do
|
18
|
+
|
19
|
+
context "when 'y' for presence" do
|
20
|
+
|
21
|
+
context "when 0 for uniqueness" do
|
22
|
+
it "sets validator to validates_presence_of" do
|
23
|
+
set_stream(StringIO.new("y\n0\n"))
|
24
|
+
subject.get_user_inputs
|
25
|
+
expect(subject.validator).to eq "validates_presence_of"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
context "when '1' for uniqueness" do
|
29
|
+
it "sets validator to validates_uniqueness_of" do
|
30
|
+
set_stream(StringIO.new("y\nr\n1\n"))
|
31
|
+
subject.get_user_inputs
|
32
|
+
expect(subject.validator).to eq "validates_uniqueness_of"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context "when '2' for uniqueness" do
|
36
|
+
it "sets validator to validates_uniqueness_scoped_to" do
|
37
|
+
set_stream(StringIO.new("y\n 2\n order_id\n"))
|
38
|
+
subject.get_user_inputs
|
39
|
+
expect(subject.validator).to eq "validates_uniqueness_scoped_to"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
context "when n for presence" do
|
44
|
+
|
45
|
+
fit "sets validator to validates_presence_of" do
|
46
|
+
set_stream(StringIO.new("n\n"))
|
47
|
+
subject.get_user_inputs
|
48
|
+
expect(subject.validator).to be nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#get_type_based_validations" do
|
54
|
+
context "when type is integer" do
|
55
|
+
|
56
|
+
before do
|
57
|
+
@chosen = RailsZen::ChosenAttr.new("phone", "integer")
|
58
|
+
end
|
59
|
+
context "when just the numericality is chosen" do
|
60
|
+
fit "sets type_based_validators to validate_numericality" do
|
61
|
+
set_stream(StringIO.new("1\n"))
|
62
|
+
@chosen.get_type_based_validations
|
63
|
+
expect(@chosen.type_based_validators).to eq "validate_numericality"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
context "when numericality with integer is chosen" do
|
67
|
+
it "sets type_based_validators to validate_integer" do
|
68
|
+
set_stream(StringIO.new("2\n"))
|
69
|
+
@chosen.get_type_based_validations
|
70
|
+
expect(@chosen.type_based_validators).to eq "validate_integer"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context "when type is a relation" do
|
75
|
+
before do
|
76
|
+
@chosen = RailsZen::ChosenAttr.new("user", "belongs_to")
|
77
|
+
end
|
78
|
+
it "sets type_based_validators to validate_belongs_to" do
|
79
|
+
@chosen.get_type_based_validations
|
80
|
+
expect(@chosen.type_based_validators).to eq "validate_belongs_to"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_stream(input)
|
88
|
+
#inp = $terminal.instance_variable_get(:@input)
|
89
|
+
#inp.flush
|
90
|
+
$terminal.instance_variable_set(:@input, input)
|
91
|
+
# highline set input, $stdin setting is not working. i.e $stdin = input
|
92
|
+
end
|