Skip to content

Commit

Permalink
add explicit injection source classes
Browse files Browse the repository at this point in the history
  • Loading branch information
garybernhardt committed Feb 11, 2012
1 parent 7c7630e commit 0de02b0
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 64 deletions.
76 changes: 51 additions & 25 deletions lib/raptor/injector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ module Raptor
# have to
#
class Injector
class UnknownInjectable < RuntimeError
def initialize(name)
super("Unknown injectable name: #{name.inspect}")
end
end

def initialize(sources)
@sources = sources
end

def self.for_request(request, route_path)
sources = InjectionSources.new(request, route_path).to_hash
sources = Injectables::All.new(request, route_path).sources
new(sources)
end

Expand All @@ -21,7 +27,8 @@ def args(method)
parameters(method).select do |type, name|
name && type != :rest && type != :block
end.map do |type, name|
@sources.fetch(name)
source_proc = @sources[name] or raise UnknownInjectable.new(name)
source_proc.call
end
end

Expand All @@ -38,40 +45,59 @@ def injection_method(method)
end

def add_record(record)
sources = @sources.merge(:record => record)
sources = @sources.merge(:record => lambda { record })
Injector.new(sources)
end
end

class InjectionSources
def initialize(request, route_path)
@request = request
@route_path = route_path
end
module Injectables
class All
def initialize(request, route_path)
@injectables = [Request.new(request),
RouteVariable.new(request, route_path)]
end

def to_hash
request_sources.merge(path_arg_sources)
def sources
@injectables.map(&:sources).inject(&:merge)
end
end

def request_sources
{:path => @request.path_info,
:params => @request.params,
:http_method => @request.request_method,
:request => @request}
end
class Request
def initialize(request)
@request = request
end

def path_arg_sources
args = {}
path_component_pairs.select do |route_component, path_component|
route_component[0] == ':'
end.each do |x, y|
args[x[1..-1].to_sym] = y.to_i
def sources
{:request => lambda { @request },
:http_method => lambda { @request.request_method },
:path => lambda { @request.path_info },
:params => lambda { @request.params }
}
end
args
end

def path_component_pairs
@route_path.split('/').zip(@request.path_info.split('/'))
class RouteVariable
def initialize(request, route_path)
@request = request
@route_path = route_path
end

def sources
Hash[path_component_pairs.map do |name, value|
[name, lambda { value }]
end]
end

def path_component_pairs
all_pairs = @route_path.split('/').zip(@request.path_info.split('/'))
variable_pairs = all_pairs.select do |name, value|
name =~ /^:/
end
variable_pairs_without_colons = variable_pairs.map do |name, value|
[name.sub(/^:/, "").to_sym, value]
end
variable_pairs_without_colons
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/delegation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def self.a_method

describe Raptor::Delegator do
it "returns nil if the delegate is nil" do
Raptor::InjectionSources.stub(:new) { stub(:to_hash => {}) }
Raptor::Injectables::All.stub(:new) { stub(:sources => {}) }
request = stub("request")
route_path = "/my_resource"
delegator = Raptor::Delegator.new(AModule, nil)
Expand Down
58 changes: 58 additions & 0 deletions spec/injectables_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require "rack"
require_relative "spec_helper"
require_relative "../lib/raptor/injector"

describe Raptor::Injectables::Request do
let(:req) do
request("POST", "/the/path", StringIO.new("param=value"))
end
subject { Raptor::Injectables::Request.new(req) }

it "injects the whole request" do
subject.sources.fetch(:request).call.should == req
end

it "injects the HTTP method" do
subject.sources.fetch(:http_method).call.should == "POST"
end

it "injects the path" do
subject.sources.fetch(:path).call.should == "/the/path"
end

it "injects request params" do
subject.sources.fetch(:params).call.should == {"param" => "value"}
end
end

describe Raptor::Injectables::RouteVariable do
it "injects IDs from paths" do
req = request("GET", "/posts/5")
injectable = Raptor::Injectables::RouteVariable.new(req, "/posts/:id")
injectable.sources.fetch(:id).call.should == "5"
end

it "injects the correct variables when there are multiple" do
req = request("GET", "/users/3/posts/4")
injectable = Raptor::Injectables::RouteVariable.new(
req, "/users/:user_id/posts/:post_id")
injectable.sources.fetch(:user_id).call.should == "3"
injectable.sources.fetch(:post_id).call.should == "4"
end
end

describe Raptor::Injectables::All do
let(:req) do
request("GET", "/posts/5")
end
subject { Raptor::Injectables::All.new(req, "/posts/:id") }

it "injects request injectables" do
subject.sources.fetch(:request).call.should == req
end

it "injects route variable injectables" do
subject.sources.fetch(:id).call.should == "5"
end
end

35 changes: 0 additions & 35 deletions spec/injection_sources_spec.rb

This file was deleted.

11 changes: 9 additions & 2 deletions spec/injector_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ def method_taking_only_a_block(&block); 'nothing' end
def method_taking_record(record); record; end

let(:params) { stub }
let(:sources) { {:params => params, :id => 5} }
let(:sources) do
{:params => lambda { params }, :id => lambda { 5 } }
end
let(:injector) { Raptor::Injector.new(sources) }

it "injects required arguments for delegate methods" do
Expand Down Expand Up @@ -47,7 +49,12 @@ def method_taking_record(record); record; end
injector_with_record.call(method).should == record
end

it "throws a human-readable error when no source is found for an argument"
it "throws an error when no source is found for an argument" do
klass = Class.new { def f(unknown_argument); end }
expect do
injector.call(klass.new.method(:f))
end.to raise_error(Raptor::Injector::UnknownInjectable)
end
end

class ObjectWithInitializerTakingParams
Expand Down
3 changes: 2 additions & 1 deletion spec/route_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def self.all
[new(1, "record 1"), new(2, "record 2")]
end
def self.find_by_id(id)
all.find { |post| post.id == id }
all.find { |post| post.id == id.to_i }
end
def self.raises_key_error
raise KeyError
Expand Down Expand Up @@ -290,6 +290,7 @@ def self.match?(path)
it "allows overriding of the presenter class"
it "uses consistent degelate terminology instead of sometimes calling them records"
it "doesn't require .html.erb on template names"
it "includes type definitions in routes so they can be casted before injection"
end

class MatchingRequirement
Expand Down
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ def request(method, path, body="")
Rack::Request.new(env(method, path, body))
end

class InjectorNotAllowed < RuntimeError; end
def disallow_injector_use
Raptor::Injector.stub(:new).and_raise(InjectorNotAllowed)
end

# The first instantiation of these is very slow for some reason. Do it here so
# it doesn't pollute test runtimes.
Rack::Request.new({})
Expand Down

0 comments on commit 0de02b0

Please sign in to comment.