trails-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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.gitmodules +0 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +74 -0
  8. data/Rakefile +2 -0
  9. data/bin/console +14 -0
  10. data/bin/server.rb +31 -0
  11. data/bin/setup +8 -0
  12. data/bin/trails +4 -0
  13. data/dynamic-archive/.rspec +2 -0
  14. data/dynamic-archive/.ruby-version +1 -0
  15. data/dynamic-archive/Gemfile +6 -0
  16. data/dynamic-archive/Gemfile.lock +38 -0
  17. data/dynamic-archive/README.md +40 -0
  18. data/dynamic-archive/lib/associatable.rb +145 -0
  19. data/dynamic-archive/lib/base.rb +143 -0
  20. data/dynamic-archive/lib/db_connection.rb +151 -0
  21. data/dynamic-archive/lib/searchable.rb +39 -0
  22. data/dynamic-archive/lib/validatable.rb +71 -0
  23. data/dynamic-archive/spec/associatable_spec.rb +294 -0
  24. data/dynamic-archive/spec/base_spec.rb +252 -0
  25. data/dynamic-archive/spec/searchable_spec.rb +44 -0
  26. data/dynamic-archive/spec/validatable_spec.rb +81 -0
  27. data/dynamic-archive/testing_database/database.db +0 -0
  28. data/dynamic-archive/testing_database/database.rb +6 -0
  29. data/dynamic-archive/testing_database/migrations/bats_migration.sql +7 -0
  30. data/dynamic-archive/testing_database/migrations/cats_migration.sql +7 -0
  31. data/dynamic-archive/testing_database/migrations/houses_migration.sql +4 -0
  32. data/dynamic-archive/testing_database/migrations/humans_migration.sql +8 -0
  33. data/dynamic-archive/testing_database/migrations/play_times_migration.sql +8 -0
  34. data/dynamic-archive/testing_database/migrations/toys_migration.sql +4 -0
  35. data/dynamic-archive/testing_database/schema.sql +44 -0
  36. data/dynamic-archive/testing_database/seed.sql +44 -0
  37. data/lib/asset_server.rb +28 -0
  38. data/lib/cli.rb +109 -0
  39. data/lib/controller_base.rb +78 -0
  40. data/lib/exception_handler.rb +14 -0
  41. data/lib/flash.rb +24 -0
  42. data/lib/router.rb +61 -0
  43. data/lib/session.rb +28 -0
  44. data/lib/trails.rb +30 -0
  45. data/lib/version.rb +3 -0
  46. data/public/assets/application.css +231 -0
  47. data/public/assets/application.js +9 -0
  48. data/template/Gemfile +1 -0
  49. data/template/README.md +0 -0
  50. data/template/app/controllers/application_controller.rb +2 -0
  51. data/template/app/controllers/static_controller.rb +4 -0
  52. data/template/app/views/layouts/application.html.erb +15 -0
  53. data/template/app/views/static/root.html.erb +73 -0
  54. data/template/config/database.rb +6 -0
  55. data/template/config/routes.rb +17 -0
  56. data/template/db/database.db +0 -0
  57. data/template/db/database.sql +0 -0
  58. data/template/db/schema.sql +0 -0
  59. data/template/db/seed.sql +0 -0
  60. data/template/public/assets/application.css +1 -0
  61. data/template/public/assets/application.js +1 -0
  62. data/trails-mvc.gemspec +42 -0
  63. metadata +257 -0
@@ -0,0 +1,252 @@
1
+ require 'byebug'
2
+ require 'base'
3
+ require 'db_connection'
4
+ require 'securerandom'
5
+
6
+ describe DynamicArchive::Base do
7
+ before(:each) { DBConnection.reset }
8
+ after(:each) { DBConnection.reset }
9
+
10
+ context 'before ::finalize!' do
11
+ before(:each) do
12
+ class Cat < DynamicArchive::Base
13
+ end
14
+ end
15
+
16
+ after(:each) do
17
+ Object.send(:remove_const, :Cat)
18
+ end
19
+
20
+ describe '::table_name' do
21
+ it 'generates default name' do
22
+ expect(Cat.table_name).to eq('cats')
23
+ end
24
+ end
25
+
26
+ describe '::table_name=' do
27
+ it 'sets table name' do
28
+ class Human < DynamicArchive::Base
29
+ self.table_name = 'humans'
30
+ end
31
+
32
+ expect(Human.table_name).to eq('humans')
33
+
34
+ Object.send(:remove_const, :Human)
35
+ end
36
+ end
37
+
38
+ describe '::columns' do
39
+ it 'returns a list of all column names as symbols' do
40
+ expect(Cat.columns).to eq([:id, :name, :owner_id])
41
+ end
42
+
43
+ it 'only queries the DB once' do
44
+ expect(DBConnection).to(
45
+ receive(:execute2).exactly(1).times.and_call_original)
46
+ 3.times { Cat.columns }
47
+ end
48
+ end
49
+
50
+ describe '#attributes' do
51
+ it 'returns @attributes hash byref' do
52
+ cat_attributes = {name: 'Gizmo'}
53
+ c = Cat.new
54
+ c.instance_variable_set('@attributes', cat_attributes)
55
+
56
+ expect(c.attributes).to equal(cat_attributes)
57
+ end
58
+
59
+ it 'lazily initializes @attributes to an empty hash' do
60
+ c = Cat.new
61
+
62
+ expect(c.instance_variables).not_to include(:@attributes)
63
+ expect(c.attributes).to eq({})
64
+ expect(c.instance_variables).to include(:@attributes)
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'after ::finalize!' do
70
+ before(:all) do
71
+ class Cat < DynamicArchive::Base
72
+ self.finalize!
73
+ end
74
+
75
+ class Human < DynamicArchive::Base
76
+ self.table_name = 'humans'
77
+
78
+ self.finalize!
79
+ end
80
+ end
81
+
82
+ after(:all) do
83
+ Object.send(:remove_const, :Cat)
84
+ Object.send(:remove_const, :Human)
85
+ end
86
+
87
+ describe '::finalize!' do
88
+ it 'creates getter methods for each column' do
89
+ c = Cat.new
90
+ expect(c.respond_to? :something).to be false
91
+ expect(c.respond_to? :name).to be true
92
+ expect(c.respond_to? :id).to be true
93
+ expect(c.respond_to? :owner_id).to be true
94
+ end
95
+
96
+ it 'creates setter methods for each column' do
97
+ c = Cat.new
98
+ c.name = "Nick Diaz"
99
+ c.id = 209
100
+ c.owner_id = 2
101
+ expect(c.name).to eq 'Nick Diaz'
102
+ expect(c.id).to eq 209
103
+ expect(c.owner_id).to eq 2
104
+ end
105
+
106
+ it 'created getter methods read from attributes hash' do
107
+ c = Cat.new
108
+ c.instance_variable_set(:@attributes, {name: "Nick Diaz"})
109
+ expect(c.name).to eq 'Nick Diaz'
110
+ end
111
+
112
+ it 'created setter methods use attributes hash to store data' do
113
+ c = Cat.new
114
+ c.name = "Nick Diaz"
115
+
116
+ expect(c.instance_variables).to include(:@attributes)
117
+ expect(c.instance_variables).not_to include(:@name)
118
+ expect(c.attributes[:name]).to eq 'Nick Diaz'
119
+ end
120
+ end
121
+
122
+ describe '#initialize' do
123
+ it 'calls appropriate setter method for each item in params' do
124
+ # We have to set method expectations on the cat object *before*
125
+ # #initialize gets called, so we use ::allocate to create a
126
+ # blank Cat object first and then call #initialize manually.
127
+ c = Cat.allocate
128
+
129
+ expect(c).to receive(:name=).with('Don Frye')
130
+ expect(c).to receive(:id=).with(100)
131
+ expect(c).to receive(:owner_id=).with(4)
132
+
133
+ c.send(:initialize, {name: 'Don Frye', id: 100, owner_id: 4})
134
+ end
135
+
136
+ it 'throws an error when given an unknown attribute' do
137
+ expect do
138
+ Cat.new(favorite_band: 'Anybody but The Eagles')
139
+ end.to raise_error "unknown attribute 'favorite_band'"
140
+ end
141
+ end
142
+
143
+ describe '::all, ::parse_all' do
144
+ it '::all returns all the rows' do
145
+ cats = Cat.all
146
+ expect(cats.count).to eq(5)
147
+ end
148
+
149
+ it '::parse_all turns an array of hashes into objects' do
150
+ hashes = [
151
+ { name: 'cat1', owner_id: 1 },
152
+ { name: 'cat2', owner_id: 2 }
153
+ ]
154
+
155
+ cats = Cat.parse_all(hashes)
156
+ expect(cats.length).to eq(2)
157
+ hashes.each_index do |i|
158
+ expect(cats[i].name).to eq(hashes[i][:name])
159
+ expect(cats[i].owner_id).to eq(hashes[i][:owner_id])
160
+ end
161
+ end
162
+
163
+ it '::all returns a list of objects, not hashes' do
164
+ cats = Cat.all
165
+ cats.each { |cat| expect(cat).to be_instance_of(Cat) }
166
+ end
167
+ end
168
+
169
+ describe '::find' do
170
+ it 'fetches single objects by id' do
171
+ c = Cat.find(1)
172
+
173
+ expect(c).to be_instance_of(Cat)
174
+ expect(c.id).to eq(1)
175
+ end
176
+
177
+ it 'returns nil if no object has the given id' do
178
+ expect(Cat.find(123)).to be_nil
179
+ end
180
+ end
181
+
182
+ describe '#attribute_values' do
183
+ it 'returns array of values' do
184
+ cat = Cat.new(id: 123, name: 'cat1', owner_id: 1)
185
+
186
+ expect(cat.attribute_values).to eq([123, 'cat1', 1])
187
+ end
188
+ end
189
+
190
+ describe '#insert' do
191
+ let(:cat) { Cat.new(name: 'Gizmo', owner_id: 1) }
192
+
193
+ before(:each) { cat.insert }
194
+
195
+ it 'inserts a new record' do
196
+ expect(Cat.all.count).to eq(6)
197
+ end
198
+
199
+ it 'sets the id once the new record is saved' do
200
+ expect(cat.id).to eq(DBConnection.last_insert_row_id)
201
+ end
202
+
203
+ it 'creates a new record with the correct values' do
204
+ # pull the cat again
205
+ cat2 = Cat.find(cat.id)
206
+
207
+ expect(cat2.name).to eq('Gizmo')
208
+ expect(cat2.owner_id).to eq(1)
209
+ end
210
+ end
211
+
212
+ describe '#update' do
213
+ it 'saves updated attributes to the DB' do
214
+ human = Human.find(2)
215
+
216
+ human.fname = 'Matthew'
217
+ human.lname = 'von Rubens'
218
+ human.update
219
+
220
+ # pull the human again
221
+ human = Human.find(2)
222
+ expect(human.fname).to eq('Matthew')
223
+ expect(human.lname).to eq('von Rubens')
224
+ end
225
+ end
226
+
227
+ describe '#save' do
228
+ it 'calls #insert when record does not exist' do
229
+ human = Human.new
230
+ expect(human).to receive(:insert)
231
+ human.save
232
+ end
233
+
234
+ it 'calls #update when record already exists' do
235
+ human = Human.find(1)
236
+ expect(human).to receive(:update)
237
+ human.save
238
+ end
239
+ end
240
+
241
+ describe '#destroy' do
242
+ it 'deletes the record from the table' do
243
+ jimmy = Human.new(fname: "Jimmy", lname: "John")
244
+ jimmy.save
245
+ expect(jimmy.fname).to eq(Human.all.last.fname)
246
+
247
+ jimmy.destroy
248
+ expect(jimmy.fname).to_not eq(Human.all.last.fname)
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,44 @@
1
+ require 'searchable'
2
+
3
+ describe 'Searchable' do
4
+ before(:each) { DBConnection.reset }
5
+ after(:each) { DBConnection.reset }
6
+
7
+ before(:all) do
8
+ class Cat < DynamicArchive::Base
9
+ finalize!
10
+ end
11
+
12
+ class Human < DynamicArchive::Base
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,81 @@
1
+ require 'validatable'
2
+
3
+ describe 'Validatable' do
4
+ before(:each) { DBConnection.reset }
5
+ after(:each) { DBConnection.reset }
6
+
7
+ before(:all) do
8
+ class Cat < DynamicArchive::Base
9
+ finalize!
10
+ end
11
+
12
+ class Human < DynamicArchive::Base
13
+ self.table_name = 'humans'
14
+ validates :fname, :lname, presence: true
15
+
16
+ finalize!
17
+ end
18
+ end
19
+
20
+ it '::validations returns a hash of hashes' do
21
+ expect(Cat.validations).to be_a(Hash)
22
+ expect(Cat.validations[:name]).to eq({})
23
+ end
24
+
25
+ it '::validations returns any validations added' do
26
+ expect(Human.validations[:fname]).to eq({ presence: true })
27
+ end
28
+
29
+ it '#save calls validations' do
30
+ alex = Human.new(fname: "Alex")
31
+ expect(Human).to receive(:presence).twice
32
+ alex.save
33
+ end
34
+
35
+ it "doesn't save invalid instances" do
36
+ alex = Human.new(fname: "Alex")
37
+ alex.save
38
+ expect(alex.errors[0]).to eq("lname did not pass presence validation")
39
+ expect(Human.all.last.fname).to_not eq("Alex")
40
+ end
41
+
42
+ it "#uniqueness does not prevent updating attributes" do
43
+ class Human
44
+ validates :fname, uniqueness: true
45
+ end
46
+
47
+ alex = Human.new(fname: "Alex", lname: "Rodriguez")
48
+ alex.save
49
+ alex.lname = "The Great"
50
+ alex.save
51
+
52
+ expect(alex.errors[0]).to be_nil
53
+ end
54
+
55
+ it "#uniqueness doesn't allow two entries with same value if true" do
56
+ class Human
57
+ validates :fname, uniqueness: true
58
+ end
59
+
60
+ alex1 = Human.new(fname: "Alex", lname: "Rodriguez")
61
+ alex1.save
62
+
63
+ alex2 = Human.new(fname: "Alex", lname: "de Ronde")
64
+ alex2.save
65
+ expect(alex2.errors[0]).to eq("fname did not pass uniqueness validation")
66
+ end
67
+
68
+ it "#uniqueness does not add flag unconstrained attributes" do
69
+ class Human
70
+ validates :fname, uniqueness: true
71
+ end
72
+
73
+ alex = Human.new(fname: "Alex", lname: "Rodriguez")
74
+ alex.save
75
+ natalie = Human.new(fname: "Natalie", lname: "Lin")
76
+ natalie.save
77
+
78
+ expect(natalie.errors[0]).to be_nil
79
+ expect(alex.errors[0]).to be_nil
80
+ end
81
+ end
@@ -0,0 +1,6 @@
1
+ ENV['ROOT_FOLDER'] = File.dirname(__FILE__)
2
+ ENV['MIGRATIONS_FOLDER'] = File.join(ENV['ROOT_FOLDER'], 'migrations')
3
+
4
+ ENV['SCHEMA_FILE'] = File.join(ENV['ROOT_FOLDER'], 'schema.sql')
5
+ ENV['SEED_FILE'] = File.join(ENV['ROOT_FOLDER'], 'seed.sql')
6
+ ENV['DB_FILE'] = File.join(ENV['ROOT_FOLDER'], 'database.db')
@@ -0,0 +1,7 @@
1
+ CREATE TABLE bats (
2
+ id INTEGER PRIMARY KEY,
3
+ name VARCHAR(255) NOT NULL,
4
+ owner_id INTEGER,
5
+
6
+ FOREIGN KEY(owner_id) REFERENCES human(id)
7
+ );
@@ -0,0 +1,7 @@
1
+ CREATE TABLE cats (
2
+ id INTEGER PRIMARY KEY,
3
+ name VARCHAR(255) NOT NULL,
4
+ owner_id INTEGER,
5
+
6
+ FOREIGN KEY(owner_id) REFERENCES human(id)
7
+ );
@@ -0,0 +1,4 @@
1
+ CREATE TABLE houses (
2
+ id INTEGER PRIMARY KEY,
3
+ address VARCHAR(255) NOT NULL
4
+ );
@@ -0,0 +1,8 @@
1
+ CREATE TABLE humans (
2
+ id INTEGER PRIMARY KEY,
3
+ fname VARCHAR(255) NOT NULL,
4
+ lname VARCHAR(255) NOT NULL,
5
+ house_id INTEGER,
6
+
7
+ FOREIGN KEY(house_id) REFERENCES human(id)
8
+ );
@@ -0,0 +1,8 @@
1
+ CREATE TABLE play_times (
2
+ id INTEGER PRIMARY KEY,
3
+ toy_id INTEGER NOT NULL,
4
+ cat_id INTEGER NOT NULL,
5
+
6
+ FOREIGN KEY(cat_id) REFERENCES cat(id),
7
+ FOREIGN KEY(toy_id) REFERENCES toy(id)
8
+ );
@@ -0,0 +1,4 @@
1
+ CREATE TABLE toys (
2
+ id INTEGER PRIMARY KEY,
3
+ name VARCHAR(255) NOT NULL
4
+ );
@@ -0,0 +1,44 @@
1
+ CREATE TABLE bats (
2
+ id INTEGER PRIMARY KEY,
3
+ name VARCHAR(255) NOT NULL,
4
+ owner_id INTEGER,
5
+
6
+ FOREIGN KEY(owner_id) REFERENCES human(id)
7
+ );
8
+
9
+ CREATE TABLE cats (
10
+ id INTEGER PRIMARY KEY,
11
+ name VARCHAR(255) NOT NULL,
12
+ owner_id INTEGER,
13
+
14
+ FOREIGN KEY(owner_id) REFERENCES human(id)
15
+ );
16
+
17
+ CREATE TABLE houses (
18
+ id INTEGER PRIMARY KEY,
19
+ address VARCHAR(255) NOT NULL
20
+ );
21
+
22
+ CREATE TABLE humans (
23
+ id INTEGER PRIMARY KEY,
24
+ fname VARCHAR(255) NOT NULL,
25
+ lname VARCHAR(255) NOT NULL,
26
+ house_id INTEGER,
27
+
28
+ FOREIGN KEY(house_id) REFERENCES human(id)
29
+ );
30
+
31
+ CREATE TABLE play_times (
32
+ id INTEGER PRIMARY KEY,
33
+ toy_id INTEGER NOT NULL,
34
+ cat_id INTEGER NOT NULL,
35
+
36
+ FOREIGN KEY(cat_id) REFERENCES cat(id),
37
+ FOREIGN KEY(toy_id) REFERENCES toy(id)
38
+ );
39
+
40
+ CREATE TABLE toys (
41
+ id INTEGER PRIMARY KEY,
42
+ name VARCHAR(255) NOT NULL
43
+ );
44
+
@@ -0,0 +1,44 @@
1
+ INSERT INTO
2
+ houses (id, address)
3
+ VALUES
4
+ (1, "26th and Guerrero"), (2, "Dolores and Market");
5
+
6
+ INSERT INTO
7
+ humans (id, fname, lname, house_id)
8
+ VALUES
9
+ (1, "Devon", "Watts", 1),
10
+ (2, "Matt", "Rubens", 1),
11
+ (3, "Ned", "Ruggeri", 2),
12
+ (4, "Catless", "Human", NULL);
13
+
14
+ INSERT INTO
15
+ cats (id, name, owner_id)
16
+ VALUES
17
+ (1, "Breakfast", 1),
18
+ (2, "Earl", 2),
19
+ (3, "Haskell", 3),
20
+ (4, "Markov", 3),
21
+ (5, "Stray Cat", NULL);
22
+
23
+ INSERT INTO
24
+ toys (id, name)
25
+ VALUES
26
+ (1, "fuzzy"),
27
+ (2, "stringy"),
28
+ (3, "mouse"),
29
+ (4, "ball"),
30
+ (5, "catnip"),
31
+ (6, "mystery");
32
+
33
+ INSERT INTO
34
+ play_times (id, toy_id, cat_id)
35
+ VALUES
36
+ (1, 1, 1),
37
+ (2, 2, 1),
38
+ (3, 3, 2),
39
+ (4, 4, 4),
40
+ (5, 5, 4),
41
+ (6, 6, 3),
42
+ (7, 1, 4),
43
+ (8, 2, 2),
44
+ (9, 4, 1);
@@ -0,0 +1,28 @@
1
+ class StaticAssetsServer
2
+ attr_reader :app, :res, :req
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ @res = Rack::Response.new
7
+ end
8
+
9
+ def call(env)
10
+ @req = Rack::Request.new(env)
11
+ if req.path =~ /^\/public\/assets/
12
+ serve_asset
13
+ else
14
+ app.call(env)
15
+ end
16
+ end
17
+
18
+ private
19
+ def serve_asset
20
+ asset = File.read(File.join(PROJECT_ROOT, req.path))
21
+
22
+ file_type = req.path.match(/\..+$/)[0]
23
+ res["Content-Type"] = Rack::Mime::MIME_TYPES[file_type]
24
+
25
+ res.write(asset)
26
+ res.finish
27
+ end
28
+ end
data/lib/cli.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'thor'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+ require 'active_support/inflector'
5
+
6
+ $thor_runner ||= false
7
+
8
+ module Trails
9
+ class Generate < Thor
10
+ desc "migration <name>", "adds a migration file to 'db/migrations'"
11
+ def migration(name)
12
+ file_name = name
13
+ timestamp = Time.now.to_i
14
+
15
+ path = File.join(PROJECT_ROOT, "db/migrations/#{timestamp}_#{file_name}.sql")
16
+ raise "Migration already created" if File.exist?(path)
17
+
18
+ File.new(path, "w")
19
+ puts "Migration #{file_name} can be found at #{path}"
20
+ end
21
+
22
+ desc "model <name>", "creates a model template file and a migration file"
23
+ def model(name)
24
+ model_path = File.join(PROJECT_ROOT, "app/models/#{name.underscore}.rb")
25
+ raise "Model already created" if File.exist?(model_path)
26
+
27
+ File.open(model_path, "w") do |file|
28
+ file.write("class #{name.camelize} < DynamicArchive::Base\n")
29
+ file.write(" self.finalize!\n")
30
+ file.write("end\n")
31
+ end
32
+
33
+ migration(name)
34
+ puts "Model #{name.camelize} can be found at #{model_path}"
35
+ end
36
+
37
+ desc "controller <name>", "creates a controller file and a view folder"
38
+ def controller(name)
39
+ file_name = name.pluralize.underscore
40
+ controller_name = "#{name.pluralize.camelize}Controller"
41
+ controller_path = File.join(PROJECT_ROOT, "app/controllers/#{file_name}_controller.rb")
42
+ view_dir = File.join(PROJECT_ROOT, "app/views/#{file_name}")
43
+ raise "Controller already created" if File.exist?(controller_path)
44
+
45
+ File.open(controller_path, "w") do |file|
46
+ file.write("class #{controller_name} < ApplicationController\n")
47
+ file.write("end\n")
48
+ end
49
+ Dir.mkdir(view_dir) unless Dir.exist?(view_dir)
50
+
51
+ puts "Controller #{controller_name} can be found at #{controller_path}"
52
+ end
53
+ end
54
+
55
+ class DB < Thor
56
+ desc "reset", "clears the database, runs all migrations, and re-seeds"
57
+ def reset
58
+ DBConnection.reset
59
+ end
60
+
61
+ desc "seed", "seeds the database"
62
+ def seed
63
+ DBConnection.seed
64
+ end
65
+
66
+ desc "migrate", "runs all pending migrations"
67
+ def migrate
68
+ DBConnection.migrate
69
+ end
70
+ end
71
+
72
+ class CLI < Thor
73
+ desc "generate COMMANDS ...ARGS", "generate new model, migration, or controller"
74
+ subcommand "generate", Generate
75
+
76
+ desc "g COMMANDS ...ARGS", "generate new model, migration, or controller"
77
+ subcommand "g", Generate
78
+
79
+ desc "db COMMANDS", "alter the database"
80
+ subcommand "db", DB
81
+
82
+ desc "server", "starts the trails server, default port: 3000"
83
+ option :port, :default => 3000
84
+ option :host, :default => "localhost"
85
+ def server
86
+ raise "Cannot find Trails App" unless PROJECT_ROOT
87
+ require_relative "#{PROJECT_ROOT}/config/routes"
88
+ require_relative '../bin/server'
89
+
90
+ server = TrailsServer.new(options[:port], options[:host])
91
+ server.start
92
+ end
93
+
94
+ desc "s", "starts the trails server, default port: 3000"
95
+ option :port, :default => 3000
96
+ option :host, :default => "localhost"
97
+ alias_method :s, :server
98
+
99
+ desc "new NAME", "creates a new Trails application skeleton in '/<name>'"
100
+ def new(name)
101
+ app_destination = File.join(Dir.pwd, name)
102
+ raise "Directory already exists at #{app_destination}" if Dir.exist?(app_destination)
103
+
104
+ FileUtils.copy_entry(TEMPLATE_PATH, app_destination)
105
+ Dir.mkdir(File.join(app_destination, "app/models"))
106
+ Dir.mkdir(File.join(app_destination, "db/migrations"))
107
+ end
108
+ end
109
+ end