grails-mvc 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.
@@ -0,0 +1,47 @@
1
+ require '00_attr_accessor_object'
2
+
3
+ describe AttrAccessorObject do
4
+ before(:all) do
5
+ class MyAttrAccessorObject < AttrAccessorObject
6
+ my_attr_accessor :x, :y
7
+ end
8
+ end
9
+
10
+ after(:all) do
11
+ Object.send(:remove_const, :MyAttrAccessorObject)
12
+ end
13
+
14
+ subject(:obj) { MyAttrAccessorObject.new }
15
+
16
+ describe '#my_attr_accessor' do
17
+ it 'defines getter methods' do
18
+ expect(obj).to respond_to(:x)
19
+ expect(obj).to respond_to(:y)
20
+ end
21
+
22
+ it 'defines setter methods' do
23
+ expect(obj).to respond_to(:x=)
24
+ expect(obj).to respond_to(:y=)
25
+ end
26
+
27
+ it 'getter methods get from associated ivars' do
28
+ x_val = 'value of @x'
29
+ y_val = 'value of @y'
30
+ obj.instance_variable_set('@x', x_val)
31
+ obj.instance_variable_set('@y', y_val)
32
+
33
+ expect(obj.x).to eq(x_val)
34
+ expect(obj.y).to eq(y_val)
35
+ end
36
+
37
+ it 'setter methods set associated ivars' do
38
+ x_val = 'value of @x'
39
+ y_val = 'value of @y'
40
+ obj.x = x_val
41
+ obj.y = y_val
42
+
43
+ expect(obj.instance_variable_get('@x')).to eq(x_val)
44
+ expect(obj.instance_variable_get('@y')).to eq(y_val)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ require '02_searchable'
2
+
3
+ describe 'Searchable' do
4
+ before(:each) { DBConnection.reset }
5
+ after(:each) { DBConnection.reset }
6
+
7
+ before(:all) do
8
+ class Cat < SQLObject
9
+ finalize!
10
+ end
11
+
12
+ class Human < SQLObject
13
+ self.table_name = 'humans'
14
+
15
+ finalize!
16
+ end
17
+ end
18
+
19
+ it '#where searches with single criterion' do
20
+ cats = Cat.where(name: 'Breakfast')
21
+ cat = cats.first
22
+
23
+ expect(cats.length).to eq(1)
24
+ expect(cat.name).to eq('Breakfast')
25
+ end
26
+
27
+ it '#where can return multiple objects' do
28
+ humans = Human.where(house_id: 1)
29
+ expect(humans.length).to eq(2)
30
+ end
31
+
32
+ it '#where searches with multiple criteria' do
33
+ humans = Human.where(fname: 'Matt', house_id: 1)
34
+ expect(humans.length).to eq(1)
35
+
36
+ human = humans[0]
37
+ expect(human.fname).to eq('Matt')
38
+ expect(human.house_id).to eq(1)
39
+ end
40
+
41
+ it '#where returns [] if nothing matches the criteria' do
42
+ expect(Human.where(fname: 'Nowhere', lname: 'Man')).to eq([])
43
+ end
44
+ end
@@ -0,0 +1,240 @@
1
+ require '01_sql_object'
2
+ require 'db_connection'
3
+ require 'securerandom'
4
+
5
+ describe SQLObject do
6
+ before(:each) { DBConnection.reset }
7
+ after(:each) { DBConnection.reset }
8
+
9
+ context 'before ::finalize!' do
10
+ before(:each) do
11
+ class Cat < SQLObject
12
+ end
13
+ end
14
+
15
+ after(:each) do
16
+ Object.send(:remove_const, :Cat)
17
+ end
18
+
19
+ describe '::table_name' do
20
+ it 'generates default name' do
21
+ expect(Cat.table_name).to eq('cats')
22
+ end
23
+ end
24
+
25
+ describe '::table_name=' do
26
+ it 'sets table name' do
27
+ class Human < SQLObject
28
+ self.table_name = 'humans'
29
+ end
30
+
31
+ expect(Human.table_name).to eq('humans')
32
+
33
+ Object.send(:remove_const, :Human)
34
+ end
35
+ end
36
+
37
+ describe '::columns' do
38
+ it 'returns a list of all column names as symbols' do
39
+ expect(Cat.columns).to eq([:id, :name, :owner_id])
40
+ end
41
+
42
+ it 'only queries the DB once' do
43
+ expect(DBConnection).to(
44
+ receive(:execute2).exactly(1).times.and_call_original)
45
+ 3.times { Cat.columns }
46
+ end
47
+ end
48
+
49
+ describe '#attributes' do
50
+ it 'returns @attributes hash byref' do
51
+ cat_attributes = {name: 'Gizmo'}
52
+ c = Cat.new
53
+ c.instance_variable_set('@attributes', cat_attributes)
54
+
55
+ expect(c.attributes).to equal(cat_attributes)
56
+ end
57
+
58
+ it 'lazily initializes @attributes to an empty hash' do
59
+ c = Cat.new
60
+
61
+ expect(c.instance_variables).not_to include(:@attributes)
62
+ expect(c.attributes).to eq({})
63
+ expect(c.instance_variables).to include(:@attributes)
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'after ::finalize!' do
69
+ before(:all) do
70
+ class Cat < SQLObject
71
+ self.finalize!
72
+ end
73
+
74
+ class Human < SQLObject
75
+ self.table_name = 'humans'
76
+
77
+ self.finalize!
78
+ end
79
+ end
80
+
81
+ after(:all) do
82
+ Object.send(:remove_const, :Cat)
83
+ Object.send(:remove_const, :Human)
84
+ end
85
+
86
+ describe '::finalize!' do
87
+ it 'creates getter methods for each column' do
88
+ c = Cat.new
89
+ expect(c.respond_to? :something).to be false
90
+ expect(c.respond_to? :name).to be true
91
+ expect(c.respond_to? :id).to be true
92
+ expect(c.respond_to? :owner_id).to be true
93
+ end
94
+
95
+ it 'creates setter methods for each column' do
96
+ c = Cat.new
97
+ c.name = "Nick Diaz"
98
+ c.id = 209
99
+ c.owner_id = 2
100
+ expect(c.name).to eq 'Nick Diaz'
101
+ expect(c.id).to eq 209
102
+ expect(c.owner_id).to eq 2
103
+ end
104
+
105
+ it 'created getter methods read from attributes hash' do
106
+ c = Cat.new
107
+ c.instance_variable_set(:@attributes, {name: "Nick Diaz"})
108
+ expect(c.name).to eq 'Nick Diaz'
109
+ end
110
+
111
+ it 'created setter methods use attributes hash to store data' do
112
+ c = Cat.new
113
+ c.name = "Nick Diaz"
114
+
115
+ expect(c.instance_variables).to include(:@attributes)
116
+ expect(c.instance_variables).not_to include(:@name)
117
+ expect(c.attributes[:name]).to eq 'Nick Diaz'
118
+ end
119
+ end
120
+
121
+ describe '#initialize' do
122
+ it 'calls appropriate setter method for each item in params' do
123
+ # We have to set method expectations on the cat object *before*
124
+ # #initialize gets called, so we use ::allocate to create a
125
+ # blank Cat object first and then call #initialize manually.
126
+ c = Cat.allocate
127
+
128
+ expect(c).to receive(:name=).with('Don Frye')
129
+ expect(c).to receive(:id=).with(100)
130
+ expect(c).to receive(:owner_id=).with(4)
131
+
132
+ c.send(:initialize, {name: 'Don Frye', id: 100, owner_id: 4})
133
+ end
134
+
135
+ it 'throws an error when given an unknown attribute' do
136
+ expect do
137
+ Cat.new(favorite_band: 'Anybody but The Eagles')
138
+ end.to raise_error "unknown attribute 'favorite_band'"
139
+ end
140
+ end
141
+
142
+ describe '::all, ::parse_all' do
143
+ it '::all returns all the rows' do
144
+ cats = Cat.all
145
+ expect(cats.count).to eq(5)
146
+ end
147
+
148
+ it '::parse_all turns an array of hashes into objects' do
149
+ hashes = [
150
+ { name: 'cat1', owner_id: 1 },
151
+ { name: 'cat2', owner_id: 2 }
152
+ ]
153
+
154
+ cats = Cat.parse_all(hashes)
155
+ expect(cats.length).to eq(2)
156
+ hashes.each_index do |i|
157
+ expect(cats[i].name).to eq(hashes[i][:name])
158
+ expect(cats[i].owner_id).to eq(hashes[i][:owner_id])
159
+ end
160
+ end
161
+
162
+ it '::all returns a list of objects, not hashes' do
163
+ cats = Cat.all
164
+ cats.each { |cat| expect(cat).to be_instance_of(Cat) }
165
+ end
166
+ end
167
+
168
+ describe '::find' do
169
+ it 'fetches single objects by id' do
170
+ c = Cat.find(1)
171
+
172
+ expect(c).to be_instance_of(Cat)
173
+ expect(c.id).to eq(1)
174
+ end
175
+
176
+ it 'returns nil if no object has the given id' do
177
+ expect(Cat.find(123)).to be_nil
178
+ end
179
+ end
180
+
181
+ describe '#attribute_values' do
182
+ it 'returns array of values' do
183
+ cat = Cat.new(id: 123, name: 'cat1', owner_id: 1)
184
+
185
+ expect(cat.attribute_values).to eq([123, 'cat1', 1])
186
+ end
187
+ end
188
+
189
+ describe '#insert' do
190
+ let(:cat) { Cat.new(name: 'Gizmo', owner_id: 1) }
191
+
192
+ before(:each) { cat.insert }
193
+
194
+ it 'inserts a new record' do
195
+ expect(Cat.all.count).to eq(6)
196
+ end
197
+
198
+ it 'sets the id once the new record is saved' do
199
+ expect(cat.id).to eq(DBConnection.last_insert_row_id)
200
+ end
201
+
202
+ it 'creates a new record with the correct values' do
203
+ # pull the cat again
204
+ cat2 = Cat.find(cat.id)
205
+
206
+ expect(cat2.name).to eq('Gizmo')
207
+ expect(cat2.owner_id).to eq(1)
208
+ end
209
+ end
210
+
211
+ describe '#update' do
212
+ it 'saves updated attributes to the DB' do
213
+ human = Human.find(2)
214
+
215
+ human.fname = 'Matthew'
216
+ human.lname = 'von Rubens'
217
+ human.update
218
+
219
+ # pull the human again
220
+ human = Human.find(2)
221
+ expect(human.fname).to eq('Matthew')
222
+ expect(human.lname).to eq('von Rubens')
223
+ end
224
+ end
225
+
226
+ describe '#save' do
227
+ it 'calls #insert when record does not exist' do
228
+ human = Human.new
229
+ expect(human).to receive(:insert)
230
+ human.save
231
+ end
232
+
233
+ it 'calls #update when record already exists' do
234
+ human = Human.find(1)
235
+ expect(human).to receive(:update)
236
+ human.save
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,40 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grails-mvc"
8
+ spec.version = Grails::VERSION
9
+ spec.authors = ["Victor Wu"]
10
+ spec.email = ["victor.wu.1@vanderbilt.edu"]
11
+
12
+ spec.summary = %q{Grails is a lightweight MVC framework based on Rails!}
13
+ spec.description = %q{Build web applications easily with Grails!}
14
+ spec.homepage = "https://github.com/victorwu3/grails/tree/master/bin"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "thor", "~> 0.20"
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.16"
36
+ spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "rspec", "~> 3.0"
38
+ spec.add_development_dependency "pry", "~> 0.10", ">= 0.10.3"
39
+ spec.add_development_dependency "activesupport", "< 5.0"
40
+ end
@@ -0,0 +1,112 @@
1
+ require 'thor'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ require_relative 'version'
5
+
6
+ module Grails
7
+
8
+ def self.project_root
9
+ current_dir = Pathname.new(Dir.pwd)
10
+ current_dir.ascend do |dir|
11
+ gemfile = File.exist?(File.join(dir, 'Gemfile'))
12
+ app_folder = Dir.exist?(File.join(dir, 'app'))
13
+ return dir if gemfile && app_folder
14
+ end
15
+ end
16
+
17
+ class Generate < Thor
18
+ desc "migration NAME", "creates a migration file with name"
19
+ def migration(name)
20
+ file_name = name
21
+ timestamp = Time.now.to_i
22
+ path = File.join(project_root, "db/migrations/#{timestamp}_#{name}.sql")
23
+ File.new(path, "w")
24
+
25
+ puts "New migration file #{name} can be found at #{path}"
26
+ end
27
+
28
+ desc "model NAME", "creates model and migration files"
29
+ def model(name)
30
+ path = File.join(project_root, "app/models/#{name.underscore}.rb")
31
+ raise "Model already exists" if File.exist?(path)
32
+
33
+ File.open(path, "w") do |file|
34
+ file.write("class #{name.camelcase}" < GrailedORM::Base\n)
35
+ file.write(" self.finalize!\n")
36
+ file.write("end\n")
37
+ end
38
+
39
+ migration(name)
40
+ puts "New migration file #{name} can be found at #{path}"
41
+ end
42
+
43
+ desc "controller NAME", "creates a controller file and view folder"
44
+ def controller(name)
45
+ controller_path = File.join(project_root, "app/controllers/#{file.pluralize.underscore}_controlller.rb"
46
+ controller_name = "#{name.pluralize.camelcase}Controller"
47
+ view_dir = File.join(project_root, "app/views/#{file.pluralize.underscore}")
48
+ raise "Controller already exists" if File.exist?(controller_path)
49
+
50
+ File.open(controller_path, "w") do |file|
51
+ file.write("class #{controller_name} < ApplicationController\n")
52
+ file.write("end")
53
+ end
54
+ Dir.mkdir(view_dir) unless Dir.exist?(view_dir)
55
+ puts "New controller file #{name} can be found at #{controller_path}"
56
+ end
57
+ end
58
+
59
+ class DB < Thor
60
+ desc "reset", "resets the database and remigrates and reseeds"
61
+ def reset
62
+ DBConnection.reset
63
+ end
64
+
65
+ desc "seed", "seeds the database with seed file"
66
+ def seed
67
+ DBConnection.seed
68
+ end
69
+
70
+ desc "migrate", "runs all pending migrations if any"
71
+ def migrate
72
+ DBConnection.migrate
73
+ end
74
+ end
75
+
76
+ class CLI < Thor
77
+ desc "new", "'new' will generate a new Grails application in the working directory"
78
+
79
+ def new(app_name)
80
+ app_dir = File.join(Dir.pwd, name)
81
+ Raise "Directory of #{app_name} already exists" if Dir.exist?(app_dir)
82
+
83
+ Dir.mkdir(File.join(app_dir, "app/models"))
84
+ Dir.mkdir(File.join(app_dir, "app/controllers"))
85
+ Dir.mkdir(File.join(app_dir, "app/views"))
86
+ Dir.mkdir(File.join(app_dir, "db/migrations"))
87
+ File.new(File.join(app_dir, "config/routes.rb"))
88
+ File.new(File.join(app_dir, "Gemfile"))
89
+
90
+ end
91
+
92
+
93
+ desc "generate COMMAND ...args", "Generates migration, model, or controller"
94
+ subcommand "g", Generate
95
+
96
+ desc "generate COMMAND ...args", "Generates migration, model, or controller"
97
+ subcommand "generate", Generate
98
+
99
+ desc "db COMMAND", "execute an action with the database"
100
+ subcommand "db", DB
101
+
102
+ desc "server", "starts a grails server, with default port of 3000"
103
+ def server
104
+ raise "Cannot find Grails app" unless project_root
105
+ require_relative "#{project_root}/config/routes"
106
+ require_relative '../bin/server'
107
+ server = GrailsServer.new(3000)
108
+ server.start
109
+ end
110
+
111
+ end
112
+ end