From 2685612a39e391cb2ee93f237f96c3776f184d04 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 11:00:44 -0500 Subject: [PATCH 01/10] Implement the ActiveRecord design pattern. --- active_record.rb | 47 ++++++++++++++++++++++++++++++++++++++++++++++ models/user.rb | 3 +++ tests/user_test.rb | 40 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 active_record.rb create mode 100644 models/user.rb create mode 100644 tests/user_test.rb diff --git a/active_record.rb b/active_record.rb new file mode 100644 index 0000000..ca63128 --- /dev/null +++ b/active_record.rb @@ -0,0 +1,47 @@ +require "connection_adapter" + +module ActiveRecord + class Base + @@connection = SqliteAdapter.new + + def initialize(attributes) + @attributes = attributes + end + + def method_missing(name, *args) + if self.class.columns.include?(name) + @attributes[name] + else + super + end + end + + def self.find(id) + find_by_sql("SELECT * FROM #{table_name} WHERE id = #{id.to_i} LIMIT 1").first + end + + def self.all + find_by_sql("SELECT * FROM #{table_name}") + end + + def self.find_by_sql(sql) + rows = @@connection.execute(sql) + rows.map do |row| + attributes = map_values_to_columns(row) + new(attributes) + end + end + + def self.map_values_to_columns(values) + Hash[columns.zip(values)] + end + + def self.columns + @@connection.columns(table_name) + end + + def self.table_name + name.downcase + "s" # users + end + end +end \ No newline at end of file diff --git a/models/user.rb b/models/user.rb new file mode 100644 index 0000000..3ef3aba --- /dev/null +++ b/models/user.rb @@ -0,0 +1,3 @@ +class User < ActiveRecord::Base + +end \ No newline at end of file diff --git a/tests/user_test.rb b/tests/user_test.rb new file mode 100644 index 0000000..3fecda3 --- /dev/null +++ b/tests/user_test.rb @@ -0,0 +1,40 @@ +require File.dirname(__FILE__) + '/test_helper' +require "active_record" +require "models/user" + +class UserTest < Test::Unit::TestCase + def test_initalize_with_attributes + user = User.new(:id => 1, :name => "Marc") + assert_equal 1, user.id + assert_equal "Marc", user.name + end + + def test_find + user = User.find(1) + assert_equal 1, user.id + end + + def test_all + user = User.all.first + assert_equal 1, user.id + end + + def test_map_values_to_columns + columns = [:id, :name] + values = [1, "Marc"] + + expected = { :id => 1, :name => "Marc" } + + attributes = User.map_values_to_columns(values) + + assert_equal expected, attributes + end + + def test_columns + assert_equal [:id, :name], User.columns + end + + def test_table_name + assert_equal "users", User.table_name + end +end \ No newline at end of file From 9b56a0e00c3f93e615f5a3731e2907df7f267851 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 11:45:11 -0500 Subject: [PATCH 02/10] Implement a first Rack app. --- config.ru | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config.ru b/config.ru index 68623d6..11bf211 100644 --- a/config.ru +++ b/config.ru @@ -1,6 +1,10 @@ class App def call(env) - # Return the response array here + [ + 200, + { "Content-Type" => "text/plain" }, + [env["PATH_INFO"]] + ] end end From 8fcdc2baa90834f8b3dd7a82ae6aa9382054a39c Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 12:07:07 -0500 Subject: [PATCH 03/10] Implement FrontController design pattern. --- config.ru | 13 +++---------- controller.rb | 3 +++ controllers/echo_controller.rb | 5 +++++ controllers/home_controller.rb | 9 +++++++++ front_controller.rb | 28 ++++++++++++++++++++++++++++ tests/front_controller_test.rb | 20 ++++++++++++++++++++ 6 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 controller.rb create mode 100644 controllers/echo_controller.rb create mode 100644 controllers/home_controller.rb create mode 100644 front_controller.rb create mode 100644 tests/front_controller_test.rb diff --git a/config.ru b/config.ru index 11bf211..da23dc3 100644 --- a/config.ru +++ b/config.ru @@ -1,11 +1,4 @@ -class App - def call(env) - [ - 200, - { "Content-Type" => "text/plain" }, - [env["PATH_INFO"]] - ] - end -end +$:.unshift "." +require "front_controller" -run App.new \ No newline at end of file +run FrontController.new \ No newline at end of file diff --git a/controller.rb b/controller.rb new file mode 100644 index 0000000..d515055 --- /dev/null +++ b/controller.rb @@ -0,0 +1,3 @@ +class Controller + attr_accessor :request, :response +end \ No newline at end of file diff --git a/controllers/echo_controller.rb b/controllers/echo_controller.rb new file mode 100644 index 0000000..aac0db5 --- /dev/null +++ b/controllers/echo_controller.rb @@ -0,0 +1,5 @@ +class EchoController < Controller + def index + response.write "You said: " + request["text"] + end +end \ No newline at end of file diff --git a/controllers/home_controller.rb b/controllers/home_controller.rb new file mode 100644 index 0000000..1681a30 --- /dev/null +++ b/controllers/home_controller.rb @@ -0,0 +1,9 @@ +class HomeController < Controller + def index + response.write "Hi from home controller" + end + + def nice + response.write "This is nice" + end +end \ No newline at end of file diff --git a/front_controller.rb b/front_controller.rb new file mode 100644 index 0000000..98501e4 --- /dev/null +++ b/front_controller.rb @@ -0,0 +1,28 @@ +require "controller" + +class FrontController + def call(env) + request = Rack::Request.new(env) + response = Rack::Response.new + + controller_name, action_name = route(request.path_info) + controller_class = load_controller_class(controller_name) + + controller = controller_class.new + controller.request = request + controller.response = response + controller.send(action_name) + + response.finish + end + + def route(path) + _, controller_name, action_name = path.split("/") # "", "home", "index" + [controller_name || "home", action_name || "index"] + end + + def load_controller_class(name) + require "controllers/#{name}_controller" + Object.const_get(name.capitalize + "Controller") # HomeController + end +end \ No newline at end of file diff --git a/tests/front_controller_test.rb b/tests/front_controller_test.rb new file mode 100644 index 0000000..83eb36b --- /dev/null +++ b/tests/front_controller_test.rb @@ -0,0 +1,20 @@ +require File.dirname(__FILE__) + '/test_helper' +require "front_controller" + +class FrontControllerTest < Test::Unit::TestCase + def setup + @front = FrontController.new + end + + def test_routing + assert_equal ["home", "index"], @front.route("/home/index") + assert_equal ["home", "index"], @front.route("/") + assert_equal ["hello", "index"], @front.route("/hello") + assert_equal ["hello", "there"], @front.route("/hello/there") + end + + def test_load_controller_class + klass = @front.load_controller_class("home") + assert_equal HomeController, klass + end +end \ No newline at end of file From a30794a870624a9ce8299518a0047207e3c037a7 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 12:20:52 -0500 Subject: [PATCH 04/10] Implement UsersController. --- controller.rb | 2 ++ controllers/users_controller.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 controllers/users_controller.rb diff --git a/controller.rb b/controller.rb index d515055..2621d74 100644 --- a/controller.rb +++ b/controller.rb @@ -1,3 +1,5 @@ +require "active_record" + class Controller attr_accessor :request, :response end \ No newline at end of file diff --git a/controllers/users_controller.rb b/controllers/users_controller.rb new file mode 100644 index 0000000..7374c90 --- /dev/null +++ b/controllers/users_controller.rb @@ -0,0 +1,14 @@ +require "models/user" + +class UsersController < Controller + def index + User.all.each do |user| + response.write "

#{user.name}

" + end + end + + def show + user = User.find(request["id"]) + response.write "

#{user.name}

" + end +end \ No newline at end of file From 3a6e30c04a27b48e97eb399ffc8d30cfa4e07a35 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 12:45:14 -0500 Subject: [PATCH 05/10] Implement before_filter/ --- controller.rb | 3 +++ controllers/home_controller.rb | 18 ++++++++++++++++++ filters.rb | 20 ++++++++++++++++++++ front_controller.rb | 4 +++- tests/filters_test.rb | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 filters.rb create mode 100644 tests/filters_test.rb diff --git a/controller.rb b/controller.rb index 2621d74..5b09e46 100644 --- a/controller.rb +++ b/controller.rb @@ -1,5 +1,8 @@ require "active_record" +require "filters" class Controller attr_accessor :request, :response + + include Filters end \ No newline at end of file diff --git a/controllers/home_controller.rb b/controllers/home_controller.rb index 1681a30..97f9abc 100644 --- a/controllers/home_controller.rb +++ b/controllers/home_controller.rb @@ -1,4 +1,8 @@ class HomeController < Controller + before_filter :header + # after_filter :footer + # around_filter :layout + def index response.write "Hi from home controller" end @@ -6,4 +10,18 @@ def index def nice response.write "This is nice" end + + def header + response.write "

My App

" + end + + def footer + response.write "

© me

" + end + + def layout + response.write "" + yield + response.write "" + end end \ No newline at end of file diff --git a/filters.rb b/filters.rb new file mode 100644 index 0000000..96a6751 --- /dev/null +++ b/filters.rb @@ -0,0 +1,20 @@ +require "active_support/all" + +module Filters + def self.included(base) + base.extend ClassMethods + base.class_attribute :before_filters + base.before_filters = [] + end + + module ClassMethods + def before_filter(method) + self.before_filters += [method] + end + end + + def filter + before_filters.each { |method| send(method) } + yield + end +end \ No newline at end of file diff --git a/front_controller.rb b/front_controller.rb index 98501e4..81d1fd1 100644 --- a/front_controller.rb +++ b/front_controller.rb @@ -11,7 +11,9 @@ def call(env) controller = controller_class.new controller.request = request controller.response = response - controller.send(action_name) + controller.filter do + controller.send(action_name) + end response.finish end diff --git a/tests/filters_test.rb b/tests/filters_test.rb new file mode 100644 index 0000000..eb132e2 --- /dev/null +++ b/tests/filters_test.rb @@ -0,0 +1,33 @@ +require File.dirname(__FILE__) + '/test_helper' +require "controller" + +class TestController < Controller + before_filter :one + before_filter :two + + def initialize(out) + @out = out + end + + def one + @out << :one + end + + def two + @out << :two + end +end + +class FiltersTest < Test::Unit::TestCase + def test_store_filters + assert_equal [:one, :two], TestController.before_filters + end + + def test_filter + out = [] + TestController.new(out).filter do + out << :process + end + assert_equal [:one, :two, :process], out + end +end \ No newline at end of file From 37cb72bf7d0283a1d9b45b943fbf5081319e9e59 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 13:08:41 -0500 Subject: [PATCH 06/10] Implement after_filter, around_filter. --- controllers/home_controller.rb | 4 ++-- filters.rb | 24 ++++++++++++++++++++++-- tests/filters_test.rb | 23 ++++++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/controllers/home_controller.rb b/controllers/home_controller.rb index 97f9abc..7c7727e 100644 --- a/controllers/home_controller.rb +++ b/controllers/home_controller.rb @@ -1,7 +1,7 @@ class HomeController < Controller before_filter :header - # after_filter :footer - # around_filter :layout + after_filter :footer + around_filter :layout def index response.write "Hi from home controller" diff --git a/filters.rb b/filters.rb index 96a6751..c0fa345 100644 --- a/filters.rb +++ b/filters.rb @@ -5,16 +5,36 @@ def self.included(base) base.extend ClassMethods base.class_attribute :before_filters base.before_filters = [] + base.class_attribute :after_filters + base.after_filters = [] + base.class_attribute :around_filters + base.around_filters = [] end module ClassMethods def before_filter(method) self.before_filters += [method] end + def after_filter(method) + self.after_filters += [method] + end + def around_filter(method) + self.around_filters += [method] + end end def filter - before_filters.each { |method| send(method) } - yield + process_proc = proc do + before_filters.each { |method| send(method) } + yield + after_filters.each { |method| send(method) } + end + + around_filters.reverse.each do |method| + proc = process_proc + process_proc = proc { send(method, &proc) } + end + + process_proc.call end end \ No newline at end of file diff --git a/tests/filters_test.rb b/tests/filters_test.rb index eb132e2..9ca7832 100644 --- a/tests/filters_test.rb +++ b/tests/filters_test.rb @@ -2,8 +2,11 @@ require "controller" class TestController < Controller + around_filter :around1 + around_filter :around2 before_filter :one before_filter :two + after_filter :three def initialize(out) @out = out @@ -16,11 +19,29 @@ def one def two @out << :two end + + def three + @out << :three + end + + def around1 + @out << "{" + yield + @out << "}" + end + + def around2 + @out << "[" + yield + @out << "]" + end end class FiltersTest < Test::Unit::TestCase def test_store_filters assert_equal [:one, :two], TestController.before_filters + assert_equal [:three], TestController.after_filters + assert_equal [:around1, :around2], TestController.around_filters end def test_filter @@ -28,6 +49,6 @@ def test_filter TestController.new(out).filter do out << :process end - assert_equal [:one, :two, :process], out + assert_equal ["{", "[", :one, :two, :process, :three, "]", "}"], out end end \ No newline at end of file From b4709445cecdcf777971ee05495dc97017a08b5a Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 13:24:30 -0500 Subject: [PATCH 07/10] Implement template view pattern. --- controller.rb | 17 +++++++++++++++++ controllers/home_controller.rb | 3 ++- tests/controller_test.rb | 22 ++++++++++++++++++++++ views/home/index.erb | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/controller_test.rb create mode 100644 views/home/index.erb diff --git a/controller.rb b/controller.rb index 5b09e46..6221da6 100644 --- a/controller.rb +++ b/controller.rb @@ -5,4 +5,21 @@ class Controller attr_accessor :request, :response include Filters + + def render(action) + response.write render_to_string(action) + end + + def render_to_string(action) + path = template_path(action) + ERB.new(File.read(path)).result(binding) + end + + def template_path(action) + File.dirname(__FILE__) + "/views/#{controller_name}/#{action}.erb" + end + + def controller_name + self.class.name[/^(\w+)Controller$/, 1].downcase # home + end end \ No newline at end of file diff --git a/controllers/home_controller.rb b/controllers/home_controller.rb index 7c7727e..18f9d5c 100644 --- a/controllers/home_controller.rb +++ b/controllers/home_controller.rb @@ -4,7 +4,8 @@ class HomeController < Controller around_filter :layout def index - response.write "Hi from home controller" + @message = "Hi from home controller" + render :index end def nice diff --git a/tests/controller_test.rb b/tests/controller_test.rb new file mode 100644 index 0000000..96c6bb3 --- /dev/null +++ b/tests/controller_test.rb @@ -0,0 +1,22 @@ +require File.dirname(__FILE__) + '/test_helper' +require "controller" +require "controllers/home_controller" + +class ControllerTest < Test::Unit::TestCase + def setup + @controller = HomeController.new + end + + def test_template_path + assert_equal File.expand_path("../../views/home/index.erb", __FILE__), + @controller.template_path("index") + end + + def test_controller_name + assert_equal "home", @controller.controller_name + end + + def test_render_to_string + assert_not_nil @controller.render_to_string("index") + end +end \ No newline at end of file diff --git a/views/home/index.erb b/views/home/index.erb new file mode 100644 index 0000000..27a2447 --- /dev/null +++ b/views/home/index.erb @@ -0,0 +1,2 @@ +

Hello from a view

+

<%= @message %>

\ No newline at end of file From 3c1dd185eeff42c5930ccaad733a3fb1e4ddd902 Mon Sep 17 00:00:00 2001 From: macournoyer Date: Tue, 31 Jan 2012 13:40:34 -0500 Subject: [PATCH 08/10] Make call to render optional. --- controller.rb | 5 +++++ front_controller.rb | 1 + 2 files changed, 6 insertions(+) diff --git a/controller.rb b/controller.rb index 6221da6..9cb11bb 100644 --- a/controller.rb +++ b/controller.rb @@ -7,9 +7,14 @@ class Controller include Filters def render(action) + @rendered = true response.write render_to_string(action) end + def rendered? + @rendered + end + def render_to_string(action) path = template_path(action) ERB.new(File.read(path)).result(binding) diff --git a/front_controller.rb b/front_controller.rb index 81d1fd1..ecb4212 100644 --- a/front_controller.rb +++ b/front_controller.rb @@ -13,6 +13,7 @@ def call(env) controller.response = response controller.filter do controller.send(action_name) + controller.render(action_name) unless controller.rendered? end response.finish From beb365d03022e56d765d1e18a63d01e74a682d49 Mon Sep 17 00:00:00 2001 From: Martin Carel Date: Tue, 31 Jan 2012 15:17:43 -0500 Subject: [PATCH 09/10] renaming a variable to improve readability ('proc' can be both a keyword and a variable name) --- filters.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/filters.rb b/filters.rb index c0fa345..2443e71 100644 --- a/filters.rb +++ b/filters.rb @@ -31,10 +31,10 @@ def filter end around_filters.reverse.each do |method| - proc = process_proc - process_proc = proc { send(method, &proc) } + current_proc = process_proc + process_proc = proc { send(method, ¤t_proc) } end process_proc.call end -end \ No newline at end of file +end From 0d687b60b03cbcdae2dac0509dcefe93c39eb779 Mon Sep 17 00:00:00 2001 From: Martin Carel Date: Tue, 31 Jan 2012 16:08:59 -0500 Subject: [PATCH 10/10] allowing each test to be run with ruby from the project dir, e.g. 'ruby tests/user_test.rb' --- tests/connection_adapter_test.rb | 4 ++-- tests/controller_test.rb | 4 ++-- tests/filters_test.rb | 4 ++-- tests/front_controller_test.rb | 4 ++-- tests/user_test.rb | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/connection_adapter_test.rb b/tests/connection_adapter_test.rb index 2f885e1..fab8609 100644 --- a/tests/connection_adapter_test.rb +++ b/tests/connection_adapter_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper' +require File.expand_path('test_helper', File.dirname(__FILE__)) require "connection_adapter" class ConnectionAdapterTest < Test::Unit::TestCase @@ -14,4 +14,4 @@ def test_execute def test_columns assert_equal [:id, :name], @adapter.columns("users") end -end \ No newline at end of file +end diff --git a/tests/controller_test.rb b/tests/controller_test.rb index 96c6bb3..15e29ad 100644 --- a/tests/controller_test.rb +++ b/tests/controller_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper' +require File.expand_path('test_helper', File.dirname(__FILE__)) require "controller" require "controllers/home_controller" @@ -19,4 +19,4 @@ def test_controller_name def test_render_to_string assert_not_nil @controller.render_to_string("index") end -end \ No newline at end of file +end diff --git a/tests/filters_test.rb b/tests/filters_test.rb index 9ca7832..cc9daa6 100644 --- a/tests/filters_test.rb +++ b/tests/filters_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper' +require File.expand_path('test_helper', File.dirname(__FILE__)) require "controller" class TestController < Controller @@ -51,4 +51,4 @@ def test_filter end assert_equal ["{", "[", :one, :two, :process, :three, "]", "}"], out end -end \ No newline at end of file +end diff --git a/tests/front_controller_test.rb b/tests/front_controller_test.rb index 83eb36b..151f4b8 100644 --- a/tests/front_controller_test.rb +++ b/tests/front_controller_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper' +require File.expand_path('test_helper', File.dirname(__FILE__)) require "front_controller" class FrontControllerTest < Test::Unit::TestCase @@ -17,4 +17,4 @@ def test_load_controller_class klass = @front.load_controller_class("home") assert_equal HomeController, klass end -end \ No newline at end of file +end diff --git a/tests/user_test.rb b/tests/user_test.rb index 3fecda3..fe78559 100644 --- a/tests/user_test.rb +++ b/tests/user_test.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper' +require File.expand_path('test_helper', File.dirname(__FILE__)) require "active_record" require "models/user" @@ -37,4 +37,4 @@ def test_columns def test_table_name assert_equal "users", User.table_name end -end \ No newline at end of file +end