rails_zen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,3 @@
1
+ module RailsZen
2
+ VERSION = "0.1.0"
3
+ 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,12 @@
1
+ module RailsZen::ModelLeveLValidation
2
+
3
+ def validate_belongs_to
4
+ ""
5
+ end
6
+ def validate_numericality
7
+ ", numericality: true"
8
+ end
9
+ def validate_integer
10
+ ", numericality: { only_integer: true }"
11
+ end
12
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ module MyGem
2
+ class Railtie < Rails::Railtie
3
+ initializer "Include your code in the controller" do
4
+ ActiveSupport.on_load(:action_controller) do
5
+ include MyGem
6
+ end
7
+ end
8
+ end
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