learn_rails 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.coveralls.yml +1 -0
- data/.gitignore +19 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +16 -0
- data/bin/learn +4 -0
- data/features/accessors.feature +122 -0
- data/features/associations.feature +506 -0
- data/features/step_definitions/associations.rb +7 -0
- data/features/support/env.rb +3 -0
- data/features/support/setup.rb +4 -0
- data/learn_rails.gemspec +31 -0
- data/lib/ext/hash.rb +5 -0
- data/lib/learn_rails.rb +54 -3
- data/lib/learn_rails/accessors.rb +56 -0
- data/lib/learn_rails/associations.rb +44 -0
- data/lib/learn_rails/associations/belongs_to.rb +48 -0
- data/lib/learn_rails/associations/has_many.rb +13 -0
- data/lib/learn_rails/associations/has_one.rb +52 -0
- data/lib/learn_rails/cli.rb +11 -0
- data/lib/learn_rails/version.rb +3 -0
- data/spec/learn_rails/accessors_spec.rb +142 -0
- data/spec/learn_rails/associations_spec.rb +575 -0
- data/spec/spec_helper.rb +4 -0
- metadata +197 -11
data/learn_rails.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'learn_rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "learn_rails"
|
8
|
+
spec.version = LearnRails::VERSION
|
9
|
+
spec.authors = ["Peter-Jan Celis"]
|
10
|
+
spec.email = ["pj@celis.org"]
|
11
|
+
spec.description = %q{Gem learn_rails shows you the ruby code behind rails magic.}
|
12
|
+
spec.summary = %q{Gem learn_rails shows you the ruby code behind rails magic.}
|
13
|
+
spec.homepage = "http://github.com/pjc/learn_rails"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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 "activesupport", "~> 4.0.1"
|
22
|
+
spec.add_dependency "thor", "~> 0.18.1"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
27
|
+
spec.add_development_dependency "cucumber", "~> 1.3.9"
|
28
|
+
spec.add_development_dependency "aruba", "~> 0.5.3"
|
29
|
+
spec.add_development_dependency "debugger", "~> 1.6.2"
|
30
|
+
spec.add_development_dependency "coveralls", "~> 0.7.0"
|
31
|
+
end
|
data/lib/ext/hash.rb
ADDED
data/lib/learn_rails.rb
CHANGED
@@ -1,3 +1,54 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require "learn_rails/version"
|
2
|
+
require "ext/hash"
|
3
|
+
require "learn_rails/associations"
|
4
|
+
require "learn_rails/associations/belongs_to"
|
5
|
+
require "learn_rails/associations/has_one"
|
6
|
+
require "learn_rails/associations/has_many"
|
7
|
+
require "learn_rails/accessors"
|
8
|
+
require "active_support/core_ext/string"
|
9
|
+
|
10
|
+
module LearnRails
|
11
|
+
def self.analyze(*magic)
|
12
|
+
if accessor? magic
|
13
|
+
remove_model_name_from_accessor magic
|
14
|
+
LearnRails::Accessors.code_for magic
|
15
|
+
elsif association? magic
|
16
|
+
ensure_model_name_for_association magic
|
17
|
+
LearnRails::Associations.code_for magic
|
18
|
+
else
|
19
|
+
error_message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.accessor? magic
|
26
|
+
(magic[0..1] & ['attr_reader', 'attr_writer', 'attr_accessor']).any?
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.remove_model_name_from_accessor magic
|
30
|
+
magic.shift unless ['attr_reader', 'attr_writer', 'attr_accessor'].include? magic[0]
|
31
|
+
magic
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.association? magic
|
35
|
+
(magic[0..1] & ['belongs_to', 'has_one', 'has_many']).any?
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ensure_model_name_for_association magic
|
39
|
+
if ['has_one', 'has_many'].include? magic[0]
|
40
|
+
puts "What is the model name for this association?"
|
41
|
+
model_name = $stdin.gets.strip
|
42
|
+
magic.unshift model_name
|
43
|
+
elsif magic[0] == 'belongs_to'
|
44
|
+
magic.unshift 'token value for params[:model]'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.error_message
|
49
|
+
<<-error.gsub(/^\s+/, '')
|
50
|
+
No ruby code available.
|
51
|
+
See http://www.github.com/pjc/learn_rails for list of valid instructions.
|
52
|
+
error
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module LearnRails
|
2
|
+
class Accessors
|
3
|
+
def self.code_for(accessor)
|
4
|
+
@accessor = accessor
|
5
|
+
@code = <<-code.gsub(/^\s+/, '')
|
6
|
+
code
|
7
|
+
|
8
|
+
attributes.each do |attribute|
|
9
|
+
@code << getter_method_for(attribute) if getter_method_needed?
|
10
|
+
@code << setter_method_for(attribute) if setter_method_needed?
|
11
|
+
end
|
12
|
+
|
13
|
+
@code
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def self.attributes
|
19
|
+
@accessor.drop(1).map { |attr| attr.delete(',').delete(':') }.delete_if(&:empty?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.getter_method_needed?
|
23
|
+
@getter ||= ['reader', 'accessor'].include? accessor_type
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.setter_method_needed?
|
27
|
+
@setter ||= ['writer', 'accessor'].include? accessor_type
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.accessor_type
|
31
|
+
@accessor[0].split('_')[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.getter_method_for attribute
|
35
|
+
<<-code.gsub(/^\s+/, '')
|
36
|
+
#{empty_comment_line_if_needed}
|
37
|
+
# def #{attribute}
|
38
|
+
# @#{attribute}
|
39
|
+
# end
|
40
|
+
code
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.setter_method_for attribute
|
44
|
+
<<-code.gsub(/^\s+/, '')
|
45
|
+
#{empty_comment_line_if_needed}
|
46
|
+
# def #{attribute}=(value)
|
47
|
+
# @#{attribute} = value
|
48
|
+
# end
|
49
|
+
code
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.empty_comment_line_if_needed
|
53
|
+
@code.present? ? '#' : nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module LearnRails
|
2
|
+
class Associations
|
3
|
+
def self.code_for(association)
|
4
|
+
params = params association
|
5
|
+
"LearnRails::Associations::#{params[:association].camelize}".constantize.send(:code_for, params)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def self.params association
|
11
|
+
clean_up association
|
12
|
+
hashify_conditions_options association
|
13
|
+
|
14
|
+
params = {}
|
15
|
+
params[:model] = association.shift.downcase
|
16
|
+
params[:association] = association.shift
|
17
|
+
params[:associate] = association.shift
|
18
|
+
|
19
|
+
options_specified = Hash[*association].symbolize_keys!
|
20
|
+
|
21
|
+
params.merge!(options_specified)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clean_up association
|
25
|
+
association.delete "=>"
|
26
|
+
association.map! { |e| e.delete(':').delete(',').delete('\"') }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.hashify_conditions_options association
|
30
|
+
return association unless association.include? "conditions"
|
31
|
+
|
32
|
+
start_options_index = association.index("conditions") + 1
|
33
|
+
end_options_index = association[start_options_index..-1].find_index { |e| e.match /}/ } + start_options_index
|
34
|
+
|
35
|
+
conditions = []
|
36
|
+
association[start_options_index..end_options_index].each do |option|
|
37
|
+
conditions << option.delete('{').delete('}') if option.length > 1
|
38
|
+
association.delete(option)
|
39
|
+
end
|
40
|
+
|
41
|
+
association.insert(start_options_index, Hash[*conditions])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module LearnRails
|
2
|
+
class Associations::BelongsTo < Associations
|
3
|
+
def self.code_for(params)
|
4
|
+
associate_model = (params[:class_name] || params[:associate]).camelize
|
5
|
+
foreign_id = params[:foreign_key] || params[:associate] + "_id"
|
6
|
+
primary_id = params[:primary_key] || "id"
|
7
|
+
|
8
|
+
<<-code.gsub(/^\s+/, '')
|
9
|
+
# def #{params[:associate]}(force_reload = false)
|
10
|
+
# @#{params[:associate]} = nil if force_reload
|
11
|
+
# @#{params[:associate]} ||= #{finder_method params, foreign_id, associate_model}
|
12
|
+
# end
|
13
|
+
#{ setter_method(params, foreign_id, primary_id) unless params[:readonly] }
|
14
|
+
#
|
15
|
+
# def build_#{params[:associate]}(attributes = {})
|
16
|
+
# self.#{params[:associate]} = #{associate_model}.new(attributes)
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def create_#{params[:associate]}(attributes = {})
|
20
|
+
# self.#{params[:associate]} = #{associate_model}.create(attributes)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def create_#{params[:associate]}!(attributes = {})
|
24
|
+
# self.#{params[:associate]} = #{associate_model}.create!(attributes)
|
25
|
+
# end
|
26
|
+
code
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def self.setter_method params, foreign_id, primary_id
|
32
|
+
<<-code.gsub(/^\s+/, '')
|
33
|
+
#
|
34
|
+
# def #{params[:associate]}=(#{params[:associate]})
|
35
|
+
# self.#{foreign_id} = #{params[:associate]}.#{primary_id}
|
36
|
+
# end
|
37
|
+
code
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.finder_method params, foreign_id, associate_model
|
41
|
+
if params[:conditions]
|
42
|
+
"#{associate_model}.first(:conditions => {:id => self.#{foreign_id}, #{params[:conditions].to_condition_string}})"
|
43
|
+
else
|
44
|
+
"#{associate_model}.find_by_id(self.#{foreign_id})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module LearnRails
|
2
|
+
class Associations::HasMany < Associations
|
3
|
+
def self.code_for(params)
|
4
|
+
associate_model = params[:associate].singularize.camelize
|
5
|
+
|
6
|
+
<<-code.gsub(/^\s+/, '')
|
7
|
+
# def #{params[:associate]}
|
8
|
+
# #{associate_model}.where(#{params[:model]}_id: self.id)
|
9
|
+
# end
|
10
|
+
code
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module LearnRails
|
2
|
+
class Associations::HasOne < Associations
|
3
|
+
def self.code_for(params)
|
4
|
+
associate_model = (params[:class_name] || params[:associate]).camelize
|
5
|
+
foreign_id = params[:foreign_key] || params[:model] + "_id"
|
6
|
+
primary_id = params[:primary_key] || "id"
|
7
|
+
|
8
|
+
<<-code.gsub(/^\s+/, '')
|
9
|
+
# def #{params[:associate]}(force_reload = false)
|
10
|
+
# @#{params[:associate]} = nil if force_reload
|
11
|
+
# @#{params[:associate]} ||= #{finder_method params, foreign_id, primary_id, associate_model}
|
12
|
+
# end
|
13
|
+
#{ setter_method(params, foreign_id, primary_id) unless params[:readonly] }
|
14
|
+
#
|
15
|
+
# def build_#{params[:associate]}(attributes = {})
|
16
|
+
# attributes[:#{foreign_id}] = self.#{primary_id}
|
17
|
+
# #{associate_model}.new(attributes)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def create_#{params[:associate]}(attributes = {})
|
21
|
+
# attributes[:#{foreign_id}] = self.#{primary_id}
|
22
|
+
# #{associate_model}.create(attributes)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def create_#{params[:associate]}!(attributes = {})
|
26
|
+
# attributes[:#{foreign_id}] = self.#{primary_id}
|
27
|
+
# #{associate_model}.create!(attributes)
|
28
|
+
# end
|
29
|
+
code
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def self.setter_method params, foreign_id, primary_id
|
35
|
+
<<-code.gsub(/^\s+/, '')
|
36
|
+
#
|
37
|
+
# def #{params[:associate]}=(#{params[:associate]})
|
38
|
+
# #{params[:associate]}.#{foreign_id} = self.#{primary_id}
|
39
|
+
# #{params[:associate]}.save
|
40
|
+
# end
|
41
|
+
code
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.finder_method params, foreign_id, primary_id, associate_model
|
45
|
+
if params[:conditions]
|
46
|
+
"#{associate_model}.first(:conditions => {:#{foreign_id} => self.#{primary_id}, #{params[:conditions].to_condition_string}})"
|
47
|
+
else
|
48
|
+
"#{associate_model}.find_by_#{foreign_id}(self.#{primary_id})"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
@accessors
|
4
|
+
describe LearnRails::Accessors do
|
5
|
+
|
6
|
+
after(@accessors) do
|
7
|
+
LearnRails::Accessors.instance_variable_set(:@getter, nil)
|
8
|
+
LearnRails::Accessors.instance_variable_set(:@setter, nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
context "attr" do
|
12
|
+
context "reader" do
|
13
|
+
context "with one attribute" do
|
14
|
+
it "should return the correct code" do
|
15
|
+
LearnRails::Accessors.code_for(%w(attr_reader :name)).should eql attr_reader_code
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "with multiple attributes" do
|
20
|
+
it "should return the correct code" do
|
21
|
+
LearnRails::Accessors.code_for(%w(attr_reader :name, :another)).should eql attr_reader_two_attributes_code
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "writer" do
|
27
|
+
context "with one attribute" do
|
28
|
+
it "should return the correct code" do
|
29
|
+
LearnRails::Accessors.code_for(%w(attr_writer :name)).should eql attr_writer_code
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with multiple attributes" do
|
34
|
+
it "should return the correct code" do
|
35
|
+
LearnRails::Accessors.code_for(%w(attr_writer :name, :another)).should eql attr_writer_two_attributes_code
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "accessor" do
|
41
|
+
context "with one attribute" do
|
42
|
+
it "should return the correct code" do
|
43
|
+
LearnRails::Accessors.code_for(%w(attr_accessor :name)).should eql attr_accessor_code
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with multiple attributes" do
|
48
|
+
it "should return the correct code" do
|
49
|
+
LearnRails::Accessors.code_for(%w(attr_accessor :name, :another)).should eql attr_accessor_two_attributes_code
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with comma" do
|
56
|
+
context "right after the last attribute" do
|
57
|
+
it "should return the correct code" do
|
58
|
+
LearnRails::Accessors.code_for(%w(attr_accessor :name,)).should eql attr_accessor_code
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "seperated by a space" do
|
63
|
+
it "should return the correct code" do
|
64
|
+
LearnRails::Accessors.code_for(%w(attr_accessor :name ,)).should eql attr_accessor_code
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def attr_reader_code
|
72
|
+
<<-code.gsub(/^\s+/, '')
|
73
|
+
# def name
|
74
|
+
# @name
|
75
|
+
# end
|
76
|
+
code
|
77
|
+
end
|
78
|
+
|
79
|
+
def attr_writer_code
|
80
|
+
<<-code.gsub(/^\s+/, '')
|
81
|
+
# def name=(value)
|
82
|
+
# @name = value
|
83
|
+
# end
|
84
|
+
code
|
85
|
+
end
|
86
|
+
|
87
|
+
def attr_accessor_code
|
88
|
+
<<-code.gsub(/^\s+/, '')
|
89
|
+
# def name
|
90
|
+
# @name
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# def name=(value)
|
94
|
+
# @name = value
|
95
|
+
# end
|
96
|
+
code
|
97
|
+
end
|
98
|
+
|
99
|
+
def attr_reader_two_attributes_code
|
100
|
+
<<-code.gsub(/^\s+/, '')
|
101
|
+
# def name
|
102
|
+
# @name
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# def another
|
106
|
+
# @another
|
107
|
+
# end
|
108
|
+
code
|
109
|
+
end
|
110
|
+
|
111
|
+
def attr_writer_two_attributes_code
|
112
|
+
<<-code.gsub(/^\s+/, '')
|
113
|
+
# def name=(value)
|
114
|
+
# @name = value
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# def another=(value)
|
118
|
+
# @another = value
|
119
|
+
# end
|
120
|
+
code
|
121
|
+
end
|
122
|
+
|
123
|
+
def attr_accessor_two_attributes_code
|
124
|
+
<<-code.gsub(/^\s+/, '')
|
125
|
+
# def name
|
126
|
+
# @name
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# def name=(value)
|
130
|
+
# @name = value
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# def another
|
134
|
+
# @another
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# def another=(value)
|
138
|
+
# @another = value
|
139
|
+
# end
|
140
|
+
code
|
141
|
+
end
|
142
|
+
end
|