grails-mvc 0.1.8 → 0.1.91

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa5a945e025edfb7bab7051aa90ea655b86bfe9d
4
- data.tar.gz: 361fdcbb753ecf9d9690e073906c349dfe7491ee
3
+ metadata.gz: 5a8f23f63fdecec15c4b76fcb24a888e8bafdf6d
4
+ data.tar.gz: 5e71b2fcf017f9564896508e2f67b3876d9cd673
5
5
  SHA512:
6
- metadata.gz: e194125dd902e3fabdf91dcc33368e3146207f31ec8576f61b2f52fbc694aa3161cfbce862de871ccd1ebf0a55a2e571ae1f1a49535a750d67e50809e503d430
7
- data.tar.gz: 1f582d4deccabf3d16cdee70ebe82e3131384bdc5116320e693206c6d1e1e2f6daa6a43a1d3805e7ab101613f5a5bf7f82244a193a400de930886207424a6fd6
6
+ metadata.gz: c20b164478fdb30fbf637f7d056fcacb84e4246e961defb5a3bdc1065d75602ba84dbc0a7df8f2fcd1fceef42e3b02611431c343806e03f69380d03b630efc4a
7
+ data.tar.gz: 800fccd281b329b4d606adf3294953572c08aa30b279f93fb36b09fe749e8316c2cdba9f89f25a6a28f6f4900722d712325bfc2e5b5b5ad496432050bf31c69b
data/bin/grails CHANGED
@@ -1,5 +1,5 @@
1
- #!/usr/bin/env ruby -U
1
+ #!/usr/bin/env ruby
2
2
 
3
- require_relative '../lib/grails.rb'
3
+ require 'grails'
4
4
 
5
5
  Grails::CLI.start(ARGV)
File without changes
File without changes
File without changes
@@ -1,4 +1,4 @@
1
-
1
+ # -*- encoding: utf-8 -*-
2
2
  lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require "version"
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = %q{Grails is a lightweight MVC framework based on Rails!}
13
13
  spec.description = %q{Build web applications easily with Grails!}
14
- spec.homepage = "https://github.com/victorwu3/grails/tree/master/bin"
14
+ spec.homepage = "https://github.com/victorwu3/grails-mvc"
15
15
  spec.license = "MIT"
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -23,18 +23,17 @@ Gem::Specification.new do |spec|
23
23
  "public gem pushes."
24
24
  end
25
25
 
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
- f.match(%r{^(test|spec|features)/})
28
- end
26
+ spec.files = `git ls-files -z`.split("\x0")
29
27
  spec.bindir = "bin"
30
- spec.executables = ["grails"]
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
29
  spec.require_paths = ["lib"]
32
30
 
33
31
  spec.add_dependency "thor", "~> 0.20"
34
32
  spec.add_dependency "httparty", "~> 0.13"
33
+ spec.add_dependency "rake", "~> 10.0"
35
34
  spec.add_dependency "sqlite3", "~> 1.3", ">= 1.3.5"
36
35
  spec.add_dependency "rack", "~> 1.6", ">= 1.6.4"
37
- spec.add_dependency "rake", "~> 10.0"
36
+ spec.add_dependency "activesupport", "~> 4.2", ">= 4.2.5.2"
38
37
 
39
38
  spec.add_development_dependency "bundler", "~> 1.16"
40
39
  spec.add_development_dependency "rspec", "~> 3.0"
@@ -23,7 +23,7 @@ module Grails
23
23
  raise "Model already exists" if File.exist?(path)
24
24
 
25
25
  File.open(path, "w") do |file|
26
- file.write("class #{name.camelcase}" < GrailedORM::Base\n)
26
+ file.write("class #{name.camelcase} < GrailedORM::Base\n")
27
27
  file.write(" self.finalize!\n")
28
28
  file.write("end\n")
29
29
  end
@@ -34,7 +34,7 @@ module Grails
34
34
 
35
35
  desc "controller NAME", "creates a controller file and view folder"
36
36
  def controller(name)
37
- controller_path = File.join(project_root, "app/controllers/#{file.pluralize.underscore}_controlller.rb"
37
+ controller_path = File.join(project_root, "app/controllers/#{file.pluralize.underscore}_controlller.rb")
38
38
  controller_name = "#{name.pluralize.camelcase}Controller"
39
39
  view_dir = File.join(project_root, "app/views/#{file.pluralize.underscore}")
40
40
  raise "Controller already exists" if File.exist?(controller_path)
@@ -72,9 +72,6 @@ module Grails
72
72
  app_dir = File.join(Dir.pwd, name)
73
73
  Raise "Directory of #{app_name} already exists" if Dir.exist?(app_dir)
74
74
  FileUtils.copy_enyry(TEMPLATE_PATH, app_dir)
75
- Dir.mkdir(File.join(app_dir, "app/models"))
76
- Dir.mkdir(File.join(app_dir, "app/controllers"))
77
- Dir.mkdir(File.join(app_dir, "app/views"))
78
75
  Dir.mkdir(File.join(app_dir, "db/migrations"))
79
76
  File.new(File.join(app_dir, "config/routes.rb"))
80
77
  File.new(File.join(app_dir, "Gemfile"))
@@ -5,7 +5,7 @@ require_relative './session'
5
5
  require_relative './flash'
6
6
  require 'active_support/inflector'
7
7
 
8
- Module GrailsController
8
+ module GrailsController
9
9
  class Base
10
10
  attr_reader :req, :res, :params
11
11
 
@@ -19,4 +19,5 @@ class Flash
19
19
 
20
20
  def []=(key, value)
21
21
  @flash[key.to_s] = value
22
+ end
22
23
  end
@@ -0,0 +1,29 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+ require_relative 'version'
4
+
5
+
6
+ module Grails
7
+ def self.project_root
8
+ current_dir = Pathname.new(Dir.pwd)
9
+ current_dir.ascend do |dir|
10
+ gemfile = File.exist?(File.join(dir, 'Gemfile'))
11
+ app_folder = Dir.exist?(File.join(dir, 'app'))
12
+ return dir if gemfile && app_folder
13
+ end
14
+ nil
15
+ end
16
+ end
17
+
18
+ GRAILS_ROOT = /^(.+)\/lib/.match(File.dirname(__FILE__))[1]
19
+ TEMPLATE_PATH = File.join(GRAILS_ROOT, 'template')
20
+ PROJECT_ROOT = Grails.project_root
21
+
22
+ require_relative "#{PROJECT_ROOT}/config/database" if PROJECT_ROOT
23
+ require_relative 'controller_base'
24
+ require_relative 'flash'
25
+ require_relative 'router'
26
+ require_relative 'session'
27
+ require_relative 'show_exceptions'
28
+ require_relative 'static'
29
+ require_relative 'commands'
@@ -1,3 +1,3 @@
1
1
  module Grails
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.91"
3
3
  end
@@ -0,0 +1,84 @@
1
+ require 'rack'
2
+ require 'controller_base'
3
+
4
+ describe ControllerBase do
5
+ before(:all) do
6
+ class UsersController < ControllerBase
7
+ def index
8
+ end
9
+ end
10
+ end
11
+ after(:all) { Object.send(:remove_const, 'UsersController') }
12
+
13
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
14
+ let(:res) { Rack::MockResponse.new('200',{},[]) }
15
+ let(:users_controller) { UsersController.new(req, res) }
16
+
17
+ describe '#render_content' do
18
+ before(:each) do
19
+ users_controller.render_content 'somebody', 'text/html'
20
+ end
21
+
22
+ it 'sets the response content type' do
23
+ expect(res['Content-Type']).to eq('text/html')
24
+ end
25
+
26
+ it 'sets the response body' do
27
+ expect(res.body).to eq('somebody')
28
+ end
29
+
30
+ describe '#already_built_response?' do
31
+ let(:users_controller2) { UsersController.new(req, res) }
32
+
33
+ it 'is false before rendering' do
34
+ expect(users_controller2.already_built_response?).to be_falsey
35
+ end
36
+
37
+ it 'is true after rendering content' do
38
+ users_controller2.render_content 'sombody', 'text/html'
39
+ expect(users_controller2.already_built_response?).to be_truthy
40
+ end
41
+
42
+ it 'raises an error when attempting to render twice' do
43
+ users_controller2.render_content 'sombody', 'text/html'
44
+ expect do
45
+ users_controller2.render_content 'sombody', 'text/html'
46
+ end.to raise_error
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#redirect' do
52
+ before(:each) do
53
+ users_controller.redirect_to('http://www.google.com')
54
+ end
55
+
56
+ it 'sets the header' do
57
+ expect(users_controller.res.header['location']).to eq('http://www.google.com')
58
+ end
59
+
60
+ it 'sets the status' do
61
+ expect(users_controller.res.status).to eq(302)
62
+ end
63
+
64
+ describe '#already_built_response?' do
65
+ let(:users_controller2) { UsersController.new(req, res) }
66
+
67
+ it 'is false before rendering' do
68
+ expect(users_controller2.already_built_response?).to be_falsey
69
+ end
70
+
71
+ it 'is true after rendering content' do
72
+ users_controller2.redirect_to('http://google.com')
73
+ expect(users_controller2.already_built_response?).to be_truthy
74
+ end
75
+
76
+ it 'raises an error when attempting to render twice' do
77
+ users_controller2.redirect_to('http://google.com')
78
+ expect do
79
+ users_controller2.redirect_to('http://google.com')
80
+ end.to raise_error
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,92 @@
1
+ require 'rack'
2
+ require 'controller_base'
3
+ require 'router'
4
+
5
+ describe ControllerBase do
6
+ let(:req) { Rack::Request.new({'rack.input' => {}, 'REQUEST_METHOD' => 'GET'}) }
7
+ let(:res) { Rack::Response.new([], '200', {}) }
8
+ let(:controller_base) { ControllerBase.new(req, res) }
9
+
10
+ describe '#form_authenticity_token' do
11
+ it 'adds new cookie with \'authenticity_token\' name to response' do
12
+ controller_base.form_authenticity_token
13
+ cookie_str = res.headers['Set-Cookie']
14
+ cookie = Rack::Utils.parse_query(cookie_str)
15
+ expect(cookie['authenticity_token']).not_to be_nil
16
+ end
17
+
18
+ it 'returns the same token set in the cookie' do
19
+ token = controller_base.form_authenticity_token
20
+ cookie_str = res.headers['Set-Cookie']
21
+ cookie = Rack::Utils.parse_query(cookie_str)
22
+
23
+ expect(cookie['authenticity_token']).to eq(token)
24
+ end
25
+
26
+ it 'returns the same token when called multiple times in the same response' do
27
+ expect(controller_base.form_authenticity_token).to eq(controller_base.form_authenticity_token)
28
+ end
29
+ end
30
+
31
+ describe '#invoke_action' do
32
+ before(:all) do
33
+ class DummyController < ControllerBase
34
+ protect_from_forgery
35
+
36
+ def index
37
+ end
38
+ end
39
+
40
+ class CatsController < ControllerBase
41
+ def index
42
+ end
43
+ end
44
+ end
45
+
46
+ after(:all) do
47
+ Object.send(:remove_const, 'DummyController')
48
+ Object.send(:remove_const, 'CatsController')
49
+ end
50
+
51
+ it 'doesn\'t check authenticity token for a GET request' do
52
+ dummy_controller = DummyController.new(req, res)
53
+
54
+ expect(dummy_controller).not_to receive(:check_authenticity_token)
55
+ dummy_controller.invoke_action(:index)
56
+ end
57
+
58
+ it 'doesn\'t check authenticity token unless ::protect_from_forgery is called' do
59
+ nonsecure_controller = CatsController.new(req, res)
60
+
61
+ expect(nonsecure_controller).not_to receive(:check_authenticity_token)
62
+ nonsecure_controller.invoke_action(:index)
63
+ end
64
+
65
+ context 'a non-GET request' do
66
+ let(:dummy_controller) { DummyController.new(req, res) }
67
+
68
+ before(:each) do
69
+ allow(req).to receive(:request_method).and_return('POST')
70
+ end
71
+
72
+ it 'calls #check_authenticity_token' do
73
+ expect(dummy_controller).to receive(:check_authenticity_token)
74
+ dummy_controller.invoke_action(:index)
75
+ end
76
+
77
+
78
+ it 'raises an error with an invalid token for any non-GET request' do
79
+ expect { dummy_controller.invoke_action(:index) }.to raise_error('Invalid authenticity token')
80
+ end
81
+
82
+ it 'doesn\'t raise an error when a valid authenticity token is given' do
83
+
84
+ # Simulate auth token being passed in both in params from form and cookies
85
+ dummy_controller.params['authenticity_token'] = 'mocktoken'
86
+ req.env['HTTP_COOKIE'] = 'authenticity_token=mocktoken'
87
+
88
+ expect { dummy_controller.invoke_action(:index) }.not_to raise_error
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,51 @@
1
+ # require 'rack/show_exceptions'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+ require 'show_exceptions'
5
+
6
+ describe ShowExceptions do
7
+ let(:show_exceptions) { ShowExceptions.new(app) }
8
+ let(:good_dummy_app) { Proc.new {} }
9
+ let(:bad_dummy_app) { Proc.new { raise RuntimeError }}
10
+
11
+ describe '#initialize' do
12
+ it 'initializes with an app' do
13
+ mock_exception = ShowExceptions.new(good_dummy_app)
14
+ expect(mock_exception.app).to be(good_dummy_app)
15
+ end
16
+ end
17
+
18
+ describe '#call' do
19
+
20
+ let(:env) { {} }
21
+
22
+ it 'calls #call on the app' do
23
+ mock_exception = ShowExceptions.new(good_dummy_app)
24
+ expect(good_dummy_app).to receive(:call).with(env)
25
+ mock_exception.call(env)
26
+ end
27
+
28
+ context 'when an app throws an error' do
29
+ let(:mock_exception) { ShowExceptions.new(bad_dummy_app) }
30
+
31
+ it 'catches exceptions' do
32
+ expect { mock_exception.call(env) }.not_to raise_error
33
+ end
34
+
35
+ it 'sets the status code to 500' do
36
+ response = mock_exception.call(env)
37
+ expect(response[0]).to eq '500'
38
+ end
39
+
40
+ it 'sets the content type to text/html' do
41
+ response = mock_exception.call(env)
42
+ expect(response[1]).to eq({'Content-type' => 'text/html'})
43
+ end
44
+
45
+ it 'the body of the response includes the error type' do
46
+ response = mock_exception.call(env)
47
+ expect(response[2]).to include 'RuntimeError'
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,93 @@
1
+ require 'rack'
2
+ require 'flash'
3
+ require 'controller_base'
4
+
5
+ describe Flash do
6
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
7
+ let(:res) { Rack::Response.new([], '200', {}) }
8
+ let(:flash) { Flash.new(req) }
9
+
10
+ describe '#[]=' do
11
+ it 'sets data in flash' do
12
+ flash['golden gate park'] = 'bison'
13
+ expect(flash['golden gate park']).to eq('bison')
14
+ end
15
+ end
16
+
17
+ describe '#store_flash' do
18
+ before(:each) do
19
+ flash['first_key'] = 'first_val'
20
+ flash.store_flash(res)
21
+ end
22
+
23
+ it 'adds new cookie with \'_rails_lite_app_flash\' name to response' do
24
+ cookie_str = res.headers['Set-Cookie']
25
+ cookie = Rack::Utils.parse_query(cookie_str)
26
+ expect(cookie['_rails_lite_app_flash']).not_to be_nil
27
+ end
28
+
29
+ it 'stores the cookie in JSON format' do
30
+ cookie_str = res.headers['Set-Cookie']
31
+ cookie = Rack::Utils.parse_query(cookie_str)
32
+ cookie_val = cookie['_rails_lite_app_flash']
33
+ cookie_hash = JSON.parse(cookie_val)
34
+
35
+ expect(cookie_hash).to be_instance_of(Hash)
36
+ expect(cookie_hash['first_key']).to eq('first_val')
37
+ end
38
+
39
+ it 'does not persist data more than 1 request' do
40
+ second_req = Rack::Request.new({'rack.input' => {}})
41
+ second_res = Rack::Response.new([], '200', {})
42
+
43
+ cookie_str = res.headers['Set-Cookie']
44
+ cookie = Rack::Utils.parse_query(cookie_str)
45
+
46
+ second_req.cookies.merge!(cookie)
47
+
48
+ second_flash = Flash.new(second_req)
49
+ second_flash.store_flash(second_res)
50
+
51
+ second_cookie_str = second_res.headers['Set-Cookie']
52
+ second_cookie = Rack::Utils.parse_query(second_cookie_str)
53
+ second_cookie_val = second_cookie['_rails_lite_app_flash']
54
+ second_cookie_hash = JSON.parse(second_cookie_val)
55
+
56
+ expect(second_cookie_hash).not_to have_key('first_key')
57
+ end
58
+ end
59
+
60
+ describe '#[]' do
61
+ it 'reads data from flash cookie' do
62
+ cookie = { '_rails_lite_app_flash' => { 'best_pizza' => 'Arizmendi' }.to_json }
63
+ req.cookies.merge!(cookie)
64
+ updated_flash = Flash.new(req)
65
+ expect(updated_flash['best_pizza']).to eq('Arizmendi')
66
+ end
67
+
68
+ it 'can be accessed using either strings or symbols' do
69
+ flash = Flash.new(req)
70
+ flash['notice'] = 'test'
71
+ expect(flash[:notice]).to eq('test')
72
+ end
73
+ end
74
+
75
+ describe '#now' do
76
+ before(:each) do
77
+ flash.now['abc'] = 'xyz'
78
+ end
79
+
80
+ it 'reads data from flash.now' do
81
+ expect(flash['abc']).to eq('xyz')
82
+ end
83
+
84
+ it 'does not persist flash.now data' do
85
+ flash.store_flash(res)
86
+ cookie_str = res.headers['Set-Cookie']
87
+ cookie = Rack::Utils.parse_query(cookie_str)
88
+ cookie_val = cookie['_rails_lite_app_flash']
89
+ cookie_hash = JSON.parse(cookie_val)
90
+ expect(cookie_hash['abc']).to be_nil
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,64 @@
1
+ require 'rack'
2
+ require 'controller_base'
3
+ require 'router'
4
+
5
+ describe 'the symphony of things' do
6
+ let(:req) { Rack::Request.new({'rack.input' => ''}) }
7
+ let(:res) { Rack::MockResponse.new('200', [], {}) }
8
+
9
+ before(:all) do
10
+ class Ctrlr < ControllerBase
11
+ def route_render
12
+ render_content('testing', 'text/html')
13
+ end
14
+
15
+ def route_does_params
16
+ render_content("got ##{params['id']}", 'text/text')
17
+ end
18
+
19
+ def update_session
20
+ session['token'] = 'testing'
21
+ render_content('hi', 'text/html')
22
+ end
23
+ end
24
+ end
25
+ after(:all) { Object.send(:remove_const, 'Ctrlr') }
26
+
27
+ describe 'routes and params' do
28
+ it 'route instantiates controller and calls invoke action' do
29
+ route = Route.new(Regexp.new('^/statuses/(?<id>\\d+)$'), :get, Ctrlr, :route_render)
30
+ allow(req).to receive(:path) { '/statuses/1' }
31
+ allow(req).to receive(:request_method) { 'GET' }
32
+ route.run(req, res)
33
+ expect(res.body).to eq('testing')
34
+ end
35
+
36
+ it 'route adds to params' do
37
+ route = Route.new(Regexp.new('^/statuses/(?<id>\\d+)$'), :get, Ctrlr, :route_does_params)
38
+ allow(req).to receive(:path) { '/statuses/1' }
39
+ allow(req).to receive(:request_method) { 'GET' }
40
+ route.run(req, res)
41
+ expect(res.body).to eq('got #1')
42
+ end
43
+ end
44
+
45
+ describe 'controller sessions' do
46
+ let(:ctrlr) { Ctrlr.new(req, res) }
47
+
48
+ it 'exposes a session via the session method' do
49
+ expect(ctrlr.session).to be_instance_of(Session)
50
+ end
51
+
52
+ it 'saves the session after rendering content' do
53
+ ctrlr.update_session
54
+ # Currently broken when flash is used. Need to store flash in the cookie
55
+ # or change this spec.
56
+ expect(res.headers['Set-Cookie']).to_not be_empty
57
+ cookie_str = res.headers['Set-Cookie']
58
+ cookie_val = Rack::Utils.parse_query(cookie_str)
59
+ cookie_str = cookie_val['_rails_lite_app']
60
+ cookie_hash = JSON.parse(cookie_str)
61
+ expect(cookie_hash['token']).to eq('testing')
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,157 @@
1
+ require 'rack'
2
+ require 'router'
3
+ require 'controller_base'
4
+
5
+ describe Route do
6
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
7
+ let(:res) { Rack::MockResponse.new('200', {}, []) }
8
+
9
+ before(:each) do
10
+ allow(req).to receive(:request_method).and_return('GET')
11
+ end
12
+
13
+ describe '#matches?' do
14
+ it 'matches simple regular expression' do
15
+ index_route = Route.new(Regexp.new('^/users$'), :get, 'x', :x)
16
+ allow(req).to receive(:path) { '/users' }
17
+ allow(req).to receive(:request_method) { 'GET' }
18
+ expect(index_route.matches?(req)).to be_truthy
19
+ end
20
+
21
+ it 'matches regular expression with capture' do
22
+ index_route = Route.new(Regexp.new('^/users/(?<id>\\d+)$'), :get, 'x', :x)
23
+ allow(req).to receive(:path) { '/users/1' }
24
+ allow(req).to receive(:request_method) { 'GET' }
25
+ expect(index_route.matches?(req)).to be_truthy
26
+ end
27
+
28
+ it 'correctly doesn\'t match regular expression with capture' do
29
+ index_route = Route.new(Regexp.new('^/users/(?<id>\\d+)$'), :get, 'UsersController', :index)
30
+ allow(req).to receive(:path) { '/statuses/1' }
31
+ allow(req).to receive(:request_method) { 'GET' }
32
+ expect(index_route.matches?(req)).to be_falsey
33
+ end
34
+ end
35
+
36
+ describe '#run' do
37
+ before(:all) { class DummyController; end }
38
+ after(:all) { Object.send(:remove_const, 'DummyController') }
39
+
40
+ it 'instantiates controller and invokes action' do
41
+ # reader beware. hairy adventures ahead.
42
+ # this is really checking way too much implementation,
43
+ # but tests the approach recommended in the project
44
+ allow(req).to receive(:path) { '/users' }
45
+
46
+ dummy_controller_class = DummyController
47
+ dummy_controller_instance = DummyController.new
48
+ allow(dummy_controller_instance).to receive(:invoke_action)
49
+ allow(dummy_controller_class).to receive(:new).with(req, res, {}) do
50
+ dummy_controller_instance
51
+ end
52
+ expect(dummy_controller_instance).to receive(:invoke_action)
53
+ index_route = Route.new(Regexp.new('^/users$'), :get, dummy_controller_class, :index)
54
+ index_route.run(req, res)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe Router do
60
+ let(:req) { Rack::Request.new({'rack-input' => {}}) }
61
+ let(:res) { Rack::MockResponse.new('200', {}, []) }
62
+
63
+ describe '#add_route' do
64
+ it 'adds a route' do
65
+ subject.add_route(1, 2, 3, 4)
66
+ expect(subject.routes.count).to eq(1)
67
+ subject.add_route(1, 2, 3, 4)
68
+ subject.add_route(1, 2, 3, 4)
69
+ expect(subject.routes.count).to eq(3)
70
+ end
71
+ end
72
+
73
+ describe '#match' do
74
+ it 'matches a correct route' do
75
+ subject.add_route(Regexp.new('^/users$'), :get, :x, :x)
76
+ allow(req).to receive(:path) { '/users' }
77
+ allow(req).to receive(:request_method) { 'GET' }
78
+ matched = subject.match(req)
79
+ expect(matched).not_to be_nil
80
+ end
81
+
82
+ it 'doesn\'t match an incorrect route' do
83
+ subject.add_route(Regexp.new('^/users$'), :get, :x, :x)
84
+ allow(req).to receive(:path) { '/incorrect_path' }
85
+ allow(req).to receive(:request_method) { 'GET' }
86
+ matched = subject.match(req)
87
+ expect(matched).to be_nil
88
+ end
89
+ end
90
+
91
+ describe '#run' do
92
+ it 'sets status to 404 if no route is found' do
93
+ subject.add_route(Regexp.new('^/users$'), :get, :x, :x)
94
+ allow(req).to receive(:path).and_return('/incorrect_path')
95
+ allow(req).to receive(:request_method).and_return('GET')
96
+ subject.run(req, res)
97
+ expect(res.status).to eq(404)
98
+ end
99
+ end
100
+
101
+ describe 'http method (get, put, post, delete)' do
102
+ it 'adds methods get, put, post and delete' do
103
+ router = Router.new
104
+ expect((router.methods - Class.new.methods)).to include(:get)
105
+ expect((router.methods - Class.new.methods)).to include(:put)
106
+ expect((router.methods - Class.new.methods)).to include(:post)
107
+ expect((router.methods - Class.new.methods)).to include(:delete)
108
+ end
109
+
110
+ it 'adds a route when an http method method is called' do
111
+ router = Router.new
112
+ router.get Regexp.new('^/users$'), ControllerBase, :index
113
+ expect(router.routes.count).to eq(1)
114
+ end
115
+ end
116
+
117
+ describe '#draw' do
118
+ it 'calls http method methods with the route information to add the route' do
119
+ index_route = double('route')
120
+ post_route = double('route')
121
+
122
+ routes = Proc.new do
123
+ get index_route
124
+ post post_route
125
+ end
126
+
127
+ router = Router.new
128
+
129
+ expect(router).to receive(:get).with(index_route)
130
+ expect(router).to receive(:post).with(post_route)
131
+
132
+ router.draw(&routes)
133
+ end
134
+ end
135
+ end
136
+
137
+ describe 'ControllerBase#initialize' do
138
+ before(:all) do
139
+ class CatsController < ControllerBase
140
+ def index
141
+ @cats = ['Gizmo']
142
+ end
143
+ end
144
+ end
145
+
146
+ after(:all) { Object.send(:remove_const, 'CatsController') }
147
+
148
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
149
+ let(:res) { Rack::MockResponse.new('200', {}, []) }
150
+ let(:cats_controller) { CatsController.new(req, res, { 'key' => 'val' } ) }
151
+
152
+ context '#initialize' do
153
+ it 'includes route params in the params object' do
154
+ expect(cats_controller.params['key']).to eq('val')
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,110 @@
1
+ require 'rack'
2
+ require 'session'
3
+ require 'controller_base'
4
+
5
+ describe Session do
6
+ let(:req) { Rack::Request.new({ 'rack.input' => {} }) }
7
+ let(:res) { Rack::Response.new([], '200', {}) }
8
+ let(:cook) { {'_rails_lite_app' => { 'xyz' => 'abc' }.to_json} }
9
+
10
+ it 'deserializes json cookie if one exists' do
11
+ req.cookies.merge!(cook)
12
+ session = Session.new(req)
13
+ expect(session['xyz']).to eq('abc')
14
+ end
15
+
16
+ describe '#store_session' do
17
+ context 'without cookies in request' do
18
+ before(:each) do
19
+ session = Session.new(req)
20
+ session['first_key'] = 'first_val'
21
+ session.store_session(res)
22
+ end
23
+
24
+ it 'adds new cookie with \'_rails_lite_app\' name to response' do
25
+ cookie_str = res.headers['Set-Cookie']
26
+ cookie = Rack::Utils.parse_query(cookie_str)
27
+ expect(cookie['_rails_lite_app']).not_to be_nil
28
+ end
29
+
30
+ it 'stores the cookie in json format' do
31
+ cookie_str = res.headers['Set-Cookie']
32
+ cookie = Rack::Utils.parse_query(cookie_str)
33
+ cookie_val = cookie['_rails_lite_app']
34
+ cookie_hash = JSON.parse(cookie_val)
35
+ expect(cookie_hash).to be_instance_of(Hash)
36
+ end
37
+ end
38
+
39
+ context 'with cookies in request' do
40
+ before(:each) do
41
+ cook = {'_rails_lite_app' => { 'pho' => 'soup' }.to_json }
42
+ req.cookies.merge!(cook)
43
+ end
44
+
45
+ it 'reads the pre-existing cookie data into hash' do
46
+ session = Session.new(req)
47
+ expect(session['pho']).to eq('soup')
48
+ end
49
+
50
+ it 'saves new and old data to the cookie' do
51
+ session = Session.new(req)
52
+ session['machine'] = 'mocha'
53
+ session.store_session(res)
54
+ cookie_str = res['Set-Cookie']
55
+ cookie = Rack::Utils.parse_query(cookie_str)
56
+ cookie_val = cookie['_rails_lite_app']
57
+ cookie_hash = JSON.parse(cookie_val)
58
+ expect(cookie_hash['pho']).to eq('soup')
59
+ expect(cookie_hash['machine']).to eq('mocha')
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe ControllerBase do
66
+ before(:all) do
67
+ class CatsController < ControllerBase
68
+ end
69
+ end
70
+ after(:all) { Object.send(:remove_const, 'CatsController') }
71
+
72
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
73
+ let(:res) { Rack::Response.new([], '200', {}) }
74
+ let(:cats_controller) { CatsController.new(req, res) }
75
+
76
+ describe '#session' do
77
+ it 'returns a session instance' do
78
+ expect(cats_controller.session).to be_a(Session)
79
+ end
80
+
81
+ it 'returns the same instance on successive invocations' do
82
+ first_result = cats_controller.session
83
+ expect(cats_controller.session).to be(first_result)
84
+ end
85
+ end
86
+
87
+ shared_examples_for 'storing session data' do
88
+ it 'should store the session data' do
89
+ cats_controller.session['test_key'] = 'test_value'
90
+ cats_controller.send(method, *args)
91
+ cookie_str = res['Set-Cookie']
92
+ cookie = Rack::Utils.parse_query(cookie_str)
93
+ cookie_val = cookie['_rails_lite_app']
94
+ cookie_hash = JSON.parse(cookie_val)
95
+ expect(cookie_hash['test_key']).to eq('test_value')
96
+ end
97
+ end
98
+
99
+ describe '#render_content' do
100
+ let(:method) { :render_content }
101
+ let(:args) { ['test', 'text/plain'] }
102
+ include_examples 'storing session data'
103
+ end
104
+
105
+ describe '#redirect_to' do
106
+ let(:method) { :redirect_to }
107
+ let(:args) { ['http://appacademy.io'] }
108
+ include_examples 'storing session data'
109
+ end
110
+ end
@@ -0,0 +1,13 @@
1
+ require "bundler/setup"
2
+
3
+ RSpec.configure do |config|
4
+ # Enable flags like --only-failures and --next-failure
5
+ config.example_status_persistence_file_path = ".rspec_status"
6
+
7
+ # Disable RSpec exposing methods globally on `Module` and `main`
8
+ config.disable_monkey_patching!
9
+
10
+ config.expect_with :rspec do |c|
11
+ c.syntax = :expect
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+
2
+ require 'static'
3
+ require 'rack'
4
+
5
+ class DummyApp
6
+ def call(env)
7
+ [200, {'Content-Type' => 'text/plain'}, ['Hello World']]
8
+ end
9
+ end
10
+
11
+ describe Static do
12
+ let(:static) { Static.new(DummyApp.new) }
13
+ let(:request) { Rack::MockRequest.new(static) }
14
+
15
+ it 'serves files' do
16
+ res = request.get('/public/hello.txt')
17
+ expect(res.body).to match(/Hello there friend/)
18
+ end
19
+
20
+ it '404s if url root is known but it can\'t find the file' do
21
+ res = request.get('/public/nicholas.jpg')
22
+ expect(res.status).to be(404)
23
+ end
24
+ end
@@ -0,0 +1,55 @@
1
+ require 'rack'
2
+ require 'controller_base'
3
+
4
+ describe ControllerBase do
5
+ before(:all) do
6
+ class CatsController < ControllerBase
7
+ def index
8
+ @cats = ['GIZMO']
9
+ end
10
+ end
11
+ end
12
+ after(:all) { Object.send(:remove_const, 'CatsController') }
13
+
14
+ let(:req) { Rack::Request.new({'rack.input' => {}}) }
15
+ let(:res) { Rack::MockResponse.new('200', {}, []) }
16
+ let(:cats_controller) { CatsController.new(req, res) }
17
+
18
+ describe '#render' do
19
+ before(:each) do
20
+ cats_controller.index
21
+ cats_controller.render(:index)
22
+ end
23
+
24
+ it 'renders the html of the index view' do
25
+ expect(cats_controller.res.body).to include('ALL THE CATS')
26
+ expect(cats_controller.res.body).to include('<h1>')
27
+ expect(cats_controller.res['Content-Type']).to eq('text/html')
28
+ end
29
+
30
+ it 'shows the proper instance variables in the index view' do
31
+ expect(cats_controller.res.body).to include('GIZMO')
32
+ expect(cats_controller.res['Content-Type']).to eq('text/html')
33
+ end
34
+
35
+ describe '#already_built_response?' do
36
+ let(:cats_controller2) { CatsController.new(req, res) }
37
+
38
+ it 'is false before rendering' do
39
+ expect(cats_controller2.already_built_response?).to be_falsey
40
+ end
41
+
42
+ it 'is true after rendering content' do
43
+ cats_controller2.render(:index)
44
+ expect(cats_controller2.already_built_response?).to be_truthy
45
+ end
46
+
47
+ it 'raises an error when attempting to render twice' do
48
+ cats_controller2.render(:index)
49
+ expect do
50
+ cats_controller2.render(:index)
51
+ end.to raise_error
52
+ end
53
+ end
54
+ end
55
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grails-mvc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.91
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Wu
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: sqlite3
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -79,19 +93,25 @@ dependencies:
79
93
  - !ruby/object:Gem::Version
80
94
  version: 1.6.4
81
95
  - !ruby/object:Gem::Dependency
82
- name: rake
96
+ name: activesupport
83
97
  requirement: !ruby/object:Gem::Requirement
84
98
  requirements:
85
99
  - - "~>"
86
100
  - !ruby/object:Gem::Version
87
- version: '10.0'
101
+ version: '4.2'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 4.2.5.2
88
105
  type: :runtime
89
106
  prerelease: false
90
107
  version_requirements: !ruby/object:Gem::Requirement
91
108
  requirements:
92
109
  - - "~>"
93
110
  - !ruby/object:Gem::Version
94
- version: '10.0'
111
+ version: '4.2'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 4.2.5.2
95
115
  - !ruby/object:Gem::Dependency
96
116
  name: bundler
97
117
  requirement: !ruby/object:Gem::Requirement
@@ -157,8 +177,7 @@ dependencies:
157
177
  description: Build web applications easily with Grails!
158
178
  email:
159
179
  - victor.wu.1@vanderbilt.edu
160
- executables:
161
- - grails
180
+ executables: []
162
181
  extensions: []
163
182
  extra_rdoc_files: []
164
183
  files:
@@ -175,6 +194,9 @@ files:
175
194
  - bin/grails
176
195
  - bin/server.rb
177
196
  - bin/setup
197
+ - boilerplate/db/database.sql
198
+ - boilerplate/db/schema.sql
199
+ - boilerplate/db/seed.sql
178
200
  - grailedorm/.rspec
179
201
  - grailedorm/Gemfile
180
202
  - grailedorm/Gemfile.lock
@@ -192,12 +214,23 @@ files:
192
214
  - lib/commands.rb
193
215
  - lib/controller_base.rb
194
216
  - lib/flash.rb
217
+ - lib/grails.rb
195
218
  - lib/router.rb
196
219
  - lib/session.rb
197
220
  - lib/show_exceptions.rb
198
221
  - lib/static.rb
199
222
  - lib/version.rb
200
- homepage: https://github.com/victorwu3/grails/tree/master/bin
223
+ - spec/controller_spec.rb
224
+ - spec/csrf_spec.rb
225
+ - spec/exceptions_spec.rb
226
+ - spec/flash_spec.rb
227
+ - spec/integration_spec.rb
228
+ - spec/router_spec.rb
229
+ - spec/session_spec.rb
230
+ - spec/spec_helper.rb
231
+ - spec/static_spec.rb
232
+ - spec/template_spec.rb
233
+ homepage: https://github.com/victorwu3/grails-mvc
201
234
  licenses:
202
235
  - MIT
203
236
  metadata: