Skip to content
47 changes: 47 additions & 0 deletions active_record.rb
Original file line number Diff line number Diff line change
@@ -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
9 changes: 3 additions & 6 deletions config.ru
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
class App
def call(env)
# Return the response array here
end
end
$:.unshift "."
require "front_controller"

run App.new
run FrontController.new
30 changes: 30 additions & 0 deletions controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require "active_record"
require "filters"

class Controller
attr_accessor :request, :response

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)
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
5 changes: 5 additions & 0 deletions controllers/echo_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class EchoController < Controller
def index
response.write "You said: " + request["text"]
end
end
28 changes: 28 additions & 0 deletions controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class HomeController < Controller
before_filter :header
after_filter :footer
around_filter :layout

def index
@message = "Hi from home controller"
render :index
end

def nice
response.write "This is nice"
end

def header
response.write "<h1>My App</h1>"
end

def footer
response.write "<p>&copy; me</p>"
end

def layout
response.write "<html><body>"
yield
response.write "</body></html>"
end
end
14 changes: 14 additions & 0 deletions controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require "models/user"

class UsersController < Controller
def index
User.all.each do |user|
response.write "<p>#{user.name}</p>"
end
end

def show
user = User.find(request["id"])
response.write "<p>#{user.name}</p>"
end
end
40 changes: 40 additions & 0 deletions filters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require "active_support/all"

module Filters
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
process_proc = proc do
before_filters.each { |method| send(method) }
yield
after_filters.each { |method| send(method) }
end

around_filters.reverse.each do |method|
current_proc = process_proc
process_proc = proc { send(method, &current_proc) }
end

process_proc.call
end
end
31 changes: 31 additions & 0 deletions front_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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.filter do
controller.send(action_name)
controller.render(action_name) unless controller.rendered?
end

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
3 changes: 3 additions & 0 deletions models/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class User < ActiveRecord::Base

end
4 changes: 2 additions & 2 deletions tests/connection_adapter_test.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -14,4 +14,4 @@ def test_execute
def test_columns
assert_equal [:id, :name], @adapter.columns("users")
end
end
end
22 changes: 22 additions & 0 deletions tests/controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require File.expand_path('test_helper', File.dirname(__FILE__))
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
54 changes: 54 additions & 0 deletions tests/filters_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require File.expand_path('test_helper', File.dirname(__FILE__))
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
end

def one
@out << :one
end

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
out = []
TestController.new(out).filter do
out << :process
end
assert_equal ["{", "[", :one, :two, :process, :three, "]", "}"], out
end
end
20 changes: 20 additions & 0 deletions tests/front_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require File.expand_path('test_helper', File.dirname(__FILE__))
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
40 changes: 40 additions & 0 deletions tests/user_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
require File.expand_path('test_helper', File.dirname(__FILE__))
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
2 changes: 2 additions & 0 deletions views/home/index.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>Hello from a view</p>
<p><%= @message %></p>