restrack 0.0.5 → 0.0.6

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.
@@ -2,54 +2,61 @@ module RESTRack
2
2
  # The ResourceRequest class handles all incoming requests.
3
3
  class ResourceRequest
4
4
  attr_reader :request, :request_id, :input
5
- attr_accessor :mime_type, :path_stack, :resource_name, :action, :id
5
+ attr_accessor :mime_type, :url_chain
6
6
 
7
7
  # Initialize the ResourceRequest by assigning a request_id and determining the path, format, and controller of the resource.
8
8
  # Accepting options to allow us to override request_id for testing.
9
9
  def initialize(opts)
10
10
  @request = opts[:request]
11
11
  @request_id = opts[:request_id] || get_request_id
12
-
13
12
  # Write input details to logs
14
13
  RESTRack.request_log.info "{#{@request_id}} #{@request.path_info} requested from #{@request.ip}"
15
-
16
14
  RESTRack.log.debug "{#{@request_id}} Reading POST Input"
17
-
18
15
  # Pull input data from POST body
19
16
  @input = read( @request )
20
-
21
17
  # Setup up the initial routing.
22
- (@path_stack, extension) = split_extension_from( @request.path_info )
23
- @mime_type = get_mime_type_from( extension )
24
- (@resource_name, @path_stack) = get_initial_resource_from( @path_stack )
25
- (@id, @action, @path_stack) = get_id_and_action_from( @path_stack )
26
-
27
- # Verify that initial resource in the request chain is accessible at the root.
28
- raise HTTP403Forbidden unless RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].blank? or RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].include?(@resource_name)
29
- raise HTTP403Forbidden if not RESTRack::CONFIG[:ROOT_RESOURCE_DENY].blank? and RESTRack::CONFIG[:ROOT_RESOURCE_DENY].include?(@resource_name)
30
- end
31
-
32
- # Locate the correct controller of resource based on the request.
33
- # The resource requested must be a member of RESTRack application or a 404 error will be thrown by RESTRack::WebService.
34
- def locate
35
- RESTRack.log.debug "{#{@request_id}} Locating Resource"
36
- @resource = instantiate_controller
18
+ @url_chain = @request.path_info.split('/')
19
+ @url_chain.shift if @url_chain[0] == ''
20
+ # Pull extension from URL
21
+ extension = ''
22
+ unless @url_chain[-1].nil?
23
+ @url_chain[-1] = @url_chain[-1].sub(/\.([^.]*)$/) do |s|
24
+ extension = $1.downcase
25
+ '' # Return an empty string as the substitution so that the extension is removed from `@url_chain[-1]`
26
+ end
27
+ end
28
+ # Determine MIME type from extension
29
+ @mime_type = get_mime_type_from( extension )
30
+ # Pull first controller from URL
31
+ @active_resource_name = @url_chain.shift
32
+ unless @active_resource_name.nil? or RESTRack.controller_exists?(@active_resource_name)
33
+ @url_chain.unshift( @active_resource_name )
34
+ end
35
+ if @active_resource_name.nil? or not RESTRack.controller_exists?(@active_resource_name)
36
+ raise HTTP404ResourceNotFound unless RESTRack::CONFIG[:DEFAULT_RESOURCE]
37
+ @active_resource_name = RESTRack::CONFIG[:DEFAULT_RESOURCE]
38
+ end
39
+ raise HTTP403Forbidden unless RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].blank? or RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].include?(@active_resource_name)
40
+ raise HTTP403Forbidden if not RESTRack::CONFIG[:ROOT_RESOURCE_DENY].blank? and RESTRack::CONFIG[:ROOT_RESOURCE_DENY].include?(@active_resource_name)
41
+ @active_controller = instantiate_controller( @active_resource_name )
37
42
  end
38
43
 
39
- # Pass along the `call` method to the typed resource object, this must occur after a call to locate.
40
- def call
41
- RESTRack.log.debug "{#{@request_id}} Processing Request"
42
- @resource.call
44
+ def content_type
45
+ @mime_type.to_s
43
46
  end
44
47
 
45
48
  # Send out the typed resource's output, this must occur after a call to run.
46
- def output
49
+ def response
47
50
  RESTRack.log.debug "{#{@request_id}} Retrieving Output"
48
- @resource.output
51
+ package( @active_controller.call )
49
52
  end
50
53
 
51
- def content_type
52
- @mime_type.to_s
54
+ # Call the next entity in the path stack.
55
+ # Method called by controller relationship methods.
56
+ def call_controller(resource_name)
57
+ @active_resource_name = resource_name
58
+ @active_controller = instantiate_controller( resource_name.to_s.camelize )
59
+ @active_controller.call
53
60
  end
54
61
 
55
62
  private
@@ -58,18 +65,19 @@ module RESTRack
58
65
  return t.strftime('%FT%T') + '.' + t.usec.to_s
59
66
  end
60
67
 
68
+ # Pull input data from POST body
61
69
  def read(request)
62
70
  input = ''
63
- unless request.content_type.blank?
71
+ if request.content_type.blank?
72
+ input = request.body.read
73
+ else
64
74
  request_mime_type = MIME::Type.new( request.content_type )
65
75
  if request_mime_type.like?( RESTRack.mime_type_for( :JSON ) )
66
76
  input = JSON.parse( request.body.read )
67
77
  elsif request_mime_type.like?( RESTRack.mime_type_for( :XML ) )
68
- input = XmlSimple.xml_out( request.body.read )
78
+ input = XmlSimple.xml_in( request.body.read )
69
79
  elsif request_mime_type.like?( RESTRack.mime_type_for( :YAML ) )
70
80
  input = YAML.parse( request.body.read )
71
- elsif request_mime_type.like?( RESTRack.mime_type_for( :TEXT ) )
72
- input = request.body.read.to_s
73
81
  else
74
82
  input = request.body.read
75
83
  end
@@ -78,18 +86,7 @@ module RESTRack
78
86
  input
79
87
  end
80
88
 
81
- # Remove the extension from the URL if present, that will be used to determine content-type.
82
- def split_extension_from(path_stack)
83
- extension = ''
84
- unless path_stack.nil?
85
- path_stack = path_stack.sub(/\.([^.]*)$/) do |s|
86
- extension = $1.downcase
87
- '' # Return an empty string as the substitution so that the extension is removed from `path_stack`
88
- end
89
- end
90
- [path_stack, extension]
91
- end
92
-
89
+ # Determine the MIME type of the request from the extension provided.
93
90
  def get_mime_type_from(extension)
94
91
  unless extension == ''
95
92
  mime_type = RESTRack.mime_type_for( extension )
@@ -104,32 +101,47 @@ module RESTRack
104
101
  mime_type
105
102
  end
106
103
 
107
- def get_initial_resource_from(orig_path_stack)
108
- ( empty_str, resource_name, path_stack ) = orig_path_stack.split('/', 3)
109
- if resource_name.blank? or not RESTRack.resource_exists? resource_name # treat as if request to default resource
110
- raise HTTP404ResourceNotFound if RESTRack::CONFIG[:DEFAULT_RESOURCE].blank?
111
- path_stack = orig_path_stack.sub(/^\//, '')
112
- resource_name = RESTRack::CONFIG[:DEFAULT_RESOURCE]
104
+ # Called from the locate method, this method dynamically finds the class based on the URI and instantiates an object of that class via the __init method on RESTRack::ResourceController.
105
+ def instantiate_controller( resource_name )
106
+ RESTRack.log.debug "{#{@request_id}} Locating Resource #{resource_name}"
107
+ begin
108
+ return RESTRack.controller_class_for( resource_name ).__init(self)
109
+ rescue
110
+ raise HTTP404ResourceNotFound, "The resource #{RESTRack::CONFIG[:SERVICE_NAME]}::#{RESTRack.controller_name(resource_name)} could not be instantiated."
113
111
  end
114
- [resource_name, path_stack]
115
112
  end
116
113
 
117
- def get_id_and_action_from(path_stack)
118
- ( id, action, path_stack ) = (path_stack || '').split('/', 3)
119
- if [ :index, :replace, :create, :destroy ].include? id
120
- action = id
121
- id = nil
114
+ # This handles outputing properly formatted content based on the file extension in the URL.
115
+ def package(data)
116
+ if @mime_type.like?( RESTRack.mime_type_for( :JSON ) )
117
+ @output = data.to_json
118
+ elsif @mime_type.like?( RESTRack.mime_type_for( :XML ) )
119
+ if File.exists? builder_file
120
+ @output = builder_up(data)
121
+ else
122
+ @output = XmlSimple.xml_out(data, 'AttrPrefix' => true, 'XmlDeclaration' => true)
123
+ end
124
+ elsif @mime_type.like?(RESTRack.mime_type_for( :YAML ) )
125
+ @output = YAML.dump(data)
126
+ elsif @mime_type.like?(RESTRack.mime_type_for( :TEXT ) )
127
+ @output = data.to_s
128
+ else
129
+ @output = data
122
130
  end
123
- [id, action, path_stack]
124
131
  end
125
132
 
126
- # Called from the locate method, this method dynamically finds the class based on the URI and instantiates an object of that class via the __init method on RESTRack::ResourceController.
127
- def instantiate_controller
128
- begin
129
- return RESTRack.controller_class_for( @resource_name ).__init(self)
130
- rescue
131
- raise HTTP404ResourceNotFound, "The resource #{RESTRack::CONFIG[:SERVICE_NAME]}::#{RESTRack.controller_name(@resource_name)} could not be instantiated."
132
- end
133
+ # Use Builder to generate the XML.
134
+ def builder_up(data)
135
+ buffer = ''
136
+ xml = Builder::XmlMarkup.new(:target => buffer)
137
+ xml.instruct!
138
+ eval( File.new( builder_file ).read )
139
+ return buffer
140
+ end
141
+
142
+ # Builds the path to the builder file for the current controller action.
143
+ def builder_file
144
+ "#{RESTRack::CONFIG[:ROOT]}/views/#{@active_resource_name}/#{@active_controller.action}.xml.builder"
133
145
  end
134
146
 
135
147
  end # class ResourceRequest
@@ -30,19 +30,24 @@ module RESTRack
30
30
  MIME::Types.type_for(format.to_s.downcase)[0]
31
31
  end
32
32
 
33
- def self.resource_exists?(resource_name)
34
- klass = controller_class_for( resource_name )
35
- return klass.is_a?(Class)
36
- rescue NameError
37
- return false
33
+ def self.controller_exists?(resource_name)
34
+ begin
35
+ return Kernel.const_get( RESTRack::CONFIG[:SERVICE_NAME].to_sym ).const_defined?( controller_name(resource_name).to_sym )
36
+ rescue # constants can't start with numerics
37
+ return false
38
+ end
38
39
  end
39
-
40
+
40
41
  def self.controller_class_for(resource_name)
41
42
  Kernel.const_get( RESTRack::CONFIG[:SERVICE_NAME].to_sym ).const_get( controller_name(resource_name).to_sym )
42
43
  end
43
44
 
44
45
  def self.controller_name(resource_name)
45
- "#{resource_name.camelize}Controller".to_sym
46
+ "#{resource_name.to_s.camelize}Controller".to_sym
47
+ end
48
+
49
+ def self.controller_has_action?(resource_name, action)
50
+ controller_class_for(resource_name).const_defined?( action.to_sym )
46
51
  end
47
52
 
48
53
  end
@@ -1,3 +1,3 @@
1
1
  module RESTRack
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -11,9 +11,7 @@ module RESTRack
11
11
  request = Rack::Request.new(env)
12
12
  begin
13
13
  resource_request = RESTRack::ResourceRequest.new( :request => request )
14
- resource_request.locate
15
- resource_request.call
16
- response = resource_request.output
14
+ response = resource_request.response
17
15
  return valid resource_request, response
18
16
  rescue Exception => exception
19
17
  return caught resource_request, exception
data/restrack.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ['Chris St. John']
10
10
  s.email = ['chris@stjohnstudios.com']
11
- s.homepage = 'http://github.com/stjohncj'
11
+ s.homepage = 'http://github.com/stjohncj/RESTRack'
12
12
  s.summary = %q{A lightweight MVC framework developed specifically for JSON and XML REST services.}
13
13
  s.description = %q{RESTRack is a Rack based MVC framework that makes it extremely easy to
14
14
  develop RESTful data services. It is inspired by Rails, and follows a few of
@@ -32,6 +32,9 @@ Gem::Specification.new do |s|
32
32
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
33
33
  s.require_paths = ["lib"]
34
34
 
35
+ s.add_runtime_dependency 'rack'
36
+ s.add_runtime_dependency 'rack-test'
37
+ s.add_runtime_dependency 'i18n'
35
38
  s.add_runtime_dependency 'json'
36
39
  s.add_runtime_dependency 'xml-simple', '>= 1.0.13'
37
40
  s.add_runtime_dependency 'builder'
@@ -0,0 +1,13 @@
1
+ class SampleApp::BataController < RESTRack::ResourceController
2
+
3
+ def show(id)
4
+ return { :BAZ => 'HOLA!' } if id == 777
5
+ return { :BAZ => 'ALOHA!' } if id == '777'
6
+ return { :OTHER => 'YUP' }
7
+ end
8
+
9
+ def index
10
+ return [1,2,3,4,5]
11
+ end
12
+
13
+ end
@@ -1,6 +1,9 @@
1
1
  class SampleApp::FooBarController < RESTRack::ResourceController
2
2
 
3
- has_direct_relationship_to( :baz, :as => :baz ) do |id|
3
+ pass_through_to( :bata )
4
+ pass_through_to( :bata, :as => :other_bata )
5
+
6
+ has_relationship_to( :baz ) do |id|
4
7
  if id =='144'
5
8
  output = '777'
6
9
  else
@@ -9,7 +12,7 @@ class SampleApp::FooBarController < RESTRack::ResourceController
9
12
  output # You can't "return" from a Proc! It will do a "return" in the outer method. Remember a "Proc" is not a Method.
10
13
  end
11
14
 
12
- has_direct_relationship_to( :bat, :as => :slugger ) do |id|
15
+ has_relationship_to( :bat, :as => :slugger ) do |id|
13
16
  if id =='144'
14
17
  output = '777'
15
18
  else
@@ -18,9 +21,13 @@ class SampleApp::FooBarController < RESTRack::ResourceController
18
21
  output # You can't "return" from a Proc! It will do a "return" in the outer method. Remember a "Proc" is not a Method.
19
22
  end
20
23
 
21
- has_direct_relationships_to( :baza, :as => :children ) do |id|
24
+ has_relationships_to( :baza, :as => :children ) do |id|
22
25
  [1,2,3,4,5,6,7,8,9]
23
26
  end
27
+
28
+ has_defined_relationships_to( :baza, :as => :def ) do |id|
29
+ [1,8,9,17]
30
+ end
24
31
 
25
32
  has_mapped_relationships_to( :bazu, :as => :maps ) do |id|
26
33
  {
@@ -47,7 +54,19 @@ class SampleApp::FooBarController < RESTRack::ResourceController
47
54
  if id == '1234567890'
48
55
  return { :foo => 'abc', :bar => '123', 'baz' => 456, :more => { :one => 1, :two => [1,2], :three => :deep_fu } }
49
56
  end
50
- { :foo => 'bar', :baz => 123 }
57
+ if id == '42'
58
+ return {
59
+ :foo => 'abc',
60
+ :bar => 123,
61
+ :baz => {
62
+ 'one' => [1],
63
+ 'two' => ['1','2'],
64
+ 'three' => ['1', 2, {:three => 3}],
65
+ 4 => :four
66
+ }
67
+ }
68
+ end
69
+ return { :foo => 'bar', :baz => 123 }
51
70
  end
52
71
  def update(id)
53
72
  { :success => true }
@@ -59,4 +78,16 @@ class SampleApp::FooBarController < RESTRack::ResourceController
59
78
  { :success => true }
60
79
  end
61
80
 
81
+ def echo
82
+ return @resource_request.input
83
+ end
84
+
85
+ def custom_entity(id)
86
+ return id
87
+ end
88
+
89
+ def custom_collection
90
+ return [1,1,2,3,5,8,13,21,34]
91
+ end
92
+
62
93
  end
@@ -107,6 +107,29 @@ class SampleApp::TestControllerActions < Test::Unit::TestCase
107
107
  assert_equal test_val, output[2]
108
108
  end
109
109
 
110
+ def test_custom_entity_action
111
+ env = Rack::MockRequest.env_for('/foo_bar/7476/custom_entity', {
112
+ :method => 'GET'
113
+ })
114
+ output = ''
115
+ assert_nothing_raised do
116
+ output = @ws.call(env)
117
+ end
118
+ test_val = '7476'.to_json
119
+ assert_equal test_val, output[2]
120
+ end
121
+
122
+ def test_custom_collection_action
123
+ env = Rack::MockRequest.env_for('/foo_bar/custom_collection', {
124
+ :method => 'GET'
125
+ })
126
+ output = ''
127
+ assert_nothing_raised do
128
+ output = @ws.call(env)
129
+ end
130
+ test_val = [1,1,2,3,5,8,13,21,34].to_json
131
+ assert_equal test_val, output[2]
132
+ end
110
133
 
111
134
  def test_missing
112
135
  env = Rack::MockRequest.env_for('/foo_bar/144/missing', {
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'rack/test'
4
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','loader'))
5
+ require 'pp'
6
+
7
+ class SampleApp::TestControllerInputs < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @ws = SampleApp::WebService.new
11
+ end
12
+
13
+ def test_post_no_content_type
14
+ test_val = "random text" # will be converted to json because of default response type
15
+ env = Rack::MockRequest.env_for('/foo_bar/echo', {
16
+ :method => 'POST',
17
+ :input => test_val
18
+ })
19
+ output = ''
20
+ assert_nothing_raised do
21
+ output = @ws.call(env)
22
+ end
23
+ assert_equal test_val.to_json, output[2] # will be converted to json because of default response type
24
+ end
25
+
26
+ def test_post_json
27
+ test_val = { :echo => 'niner' }.to_json
28
+ env = Rack::MockRequest.env_for('/foo_bar/echo', {
29
+ :method => 'POST',
30
+ :input => test_val,
31
+ 'CONTENT_TYPE' => 'application/json'
32
+ })
33
+ output = ''
34
+ assert_nothing_raised do
35
+ output = @ws.call(env)
36
+ end
37
+ assert_equal test_val, output[2]
38
+ end
39
+ # TODO: Test all input formats
40
+
41
+ end
@@ -9,7 +9,49 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
9
9
  def setup
10
10
  @ws = SampleApp::WebService.new
11
11
  end
12
-
12
+
13
+ def test_pass_through_to
14
+ env = Rack::MockRequest.env_for('/foo_bar/144/bata/777', {
15
+ :method => 'GET'
16
+ })
17
+ output = ''
18
+ assert_nothing_raised do
19
+ output = @ws.call(env)
20
+ end
21
+ test_val = { :BAZ => 'ALOHA!' }.to_json
22
+ assert_equal test_val, output[2]
23
+
24
+ env = Rack::MockRequest.env_for('/foo_bar/133/bata/abc', {
25
+ :method => 'GET'
26
+ })
27
+ output = ''
28
+ assert_nothing_raised do
29
+ output = @ws.call(env)
30
+ end
31
+ test_val = { :OTHER => 'YUP' }.to_json
32
+ assert_equal test_val, output[2]
33
+
34
+ env = Rack::MockRequest.env_for('/foo_bar/144/bata/', {
35
+ :method => 'GET'
36
+ })
37
+ output = ''
38
+ assert_nothing_raised do
39
+ output = @ws.call(env)
40
+ end
41
+ test_val = [1,2,3,4,5].to_json
42
+ assert_equal test_val, output[2]
43
+
44
+ env = Rack::MockRequest.env_for('/foo_bar/144/other_bata', {
45
+ :method => 'GET'
46
+ })
47
+ output = ''
48
+ assert_nothing_raised do
49
+ output = @ws.call(env)
50
+ end
51
+ test_val = [1,2,3,4,5].to_json
52
+ assert_equal test_val, output[2]
53
+ end
54
+
13
55
  def test_has_relationship_to
14
56
  env = Rack::MockRequest.env_for('/foo_bar/144/baz', {
15
57
  :method => 'GET'
@@ -18,9 +60,9 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
18
60
  assert_nothing_raised do
19
61
  output = @ws.call(env)
20
62
  end
21
- test_val = { :BAZ => 'HOLA!' }.to_json
63
+ test_val = { :BAZ => 'ALOHA!' }.to_json
22
64
  assert_equal test_val, output[2]
23
-
65
+
24
66
  env = Rack::MockRequest.env_for('/foo_bar/133/baz', {
25
67
  :method => 'GET'
26
68
  })
@@ -30,7 +72,7 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
30
72
  end
31
73
  test_val = { :OTHER => 'YUP' }.to_json
32
74
  assert_equal test_val, output[2]
33
-
75
+
34
76
  env = Rack::MockRequest.env_for('/foo_bar/144/baz/', {
35
77
  :method => 'GET'
36
78
  })
@@ -38,11 +80,11 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
38
80
  assert_nothing_raised do
39
81
  output = @ws.call(env)
40
82
  end
41
- test_val = { :BAZ => 'HOLA!' }.to_json
83
+ test_val = { :BAZ => 'ALOHA!' }.to_json
42
84
  assert_equal test_val, output[2]
43
- end
44
-
45
- def test_second_has_relationship_to
85
+
86
+ #------
87
+
46
88
  env = Rack::MockRequest.env_for('/foo_bar/144/slugger', {
47
89
  :method => 'GET'
48
90
  })
@@ -50,9 +92,9 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
50
92
  assert_nothing_raised do
51
93
  output = @ws.call(env)
52
94
  end
53
- test_val = { :WOM => 'BAT!' }.to_json
95
+ test_val = { :WOM => 'NOBAT!' }.to_json
54
96
  assert_equal test_val, output[2]
55
-
97
+
56
98
  env = Rack::MockRequest.env_for('/foo_bar/133/slugger', {
57
99
  :method => 'GET'
58
100
  })
@@ -62,7 +104,7 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
62
104
  end
63
105
  test_val = { :SUHWING => 'BATTER' }.to_json
64
106
  assert_equal test_val, output[2]
65
-
107
+
66
108
  env = Rack::MockRequest.env_for('/foo_bar/144/slugger/', {
67
109
  :method => 'GET'
68
110
  })
@@ -70,12 +112,12 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
70
112
  assert_nothing_raised do
71
113
  output = @ws.call(env)
72
114
  end
73
- test_val = { :WOM => 'BAT!' }.to_json
115
+ test_val = { :WOM => 'NOBAT!' }.to_json
74
116
  assert_equal test_val, output[2]
75
117
  end
76
-
77
- def test_has_direct_relationships_to
78
- env = Rack::MockRequest.env_for('/foo_bar/133/children/1', {
118
+
119
+ def test_has_relationships_to
120
+ env = Rack::MockRequest.env_for('/foo_bar/133/children/0', {
79
121
  :method => 'GET'
80
122
  })
81
123
  output = ''
@@ -84,8 +126,8 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
84
126
  end
85
127
  test_val = { :BAZA => 'YESSIR' }.to_json
86
128
  assert_equal test_val, output[2]
87
-
88
- env = Rack::MockRequest.env_for('/foo_bar/133/children/8', {
129
+
130
+ env = Rack::MockRequest.env_for('/foo_bar/133/children/7', {
89
131
  :method => 'GET'
90
132
  })
91
133
  output = ''
@@ -94,7 +136,7 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
94
136
  end
95
137
  test_val = { :NOWAY => 'JOSE' }.to_json
96
138
  assert_equal test_val, output[2]
97
-
139
+
98
140
  env = Rack::MockRequest.env_for('/foo_bar/133/children/11', {
99
141
  :method => 'GET'
100
142
  })
@@ -104,7 +146,50 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
104
146
  end
105
147
  assert_equal 404, output[0]
106
148
  end
107
-
149
+
150
+ def test_has_defined_relationships_to
151
+ # goes to baza
152
+ env = Rack::MockRequest.env_for('/foo_bar/133/def/1', {
153
+ :method => 'GET'
154
+ })
155
+ output = ''
156
+ assert_nothing_raised do
157
+ output = @ws.call(env)
158
+ end
159
+ test_val = { :BAZA => 'YESSIR' }.to_json
160
+ assert_equal test_val, output[2]
161
+
162
+ env = Rack::MockRequest.env_for('/foo_bar/133/def/8', {
163
+ :method => 'GET'
164
+ })
165
+ output = ''
166
+ assert_nothing_raised do
167
+ output = @ws.call(env)
168
+ end
169
+ test_val = { :NOWAY => 'JOSE' }.to_json
170
+ assert_equal test_val, output[2]
171
+
172
+ # this should 404
173
+ env = Rack::MockRequest.env_for('/foo_bar/133/def/11', {
174
+ :method => 'GET'
175
+ })
176
+ output = ''
177
+ assert_nothing_raised do
178
+ output = @ws.call(env)
179
+ end
180
+ assert_equal 404, output[0]
181
+
182
+ # this should 400
183
+ env = Rack::MockRequest.env_for('/foo_bar/133/def', {
184
+ :method => 'GET'
185
+ })
186
+ output = ''
187
+ assert_nothing_raised do
188
+ output = @ws.call(env)
189
+ end
190
+ assert_equal 400, output[0]
191
+ end
192
+
108
193
  def test_has_mapped_relationships_to
109
194
  env = Rack::MockRequest.env_for('/foo_bar/133/maps/first', {
110
195
  :method => 'GET'
@@ -115,7 +200,7 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
115
200
  end
116
201
  test_val = '1'
117
202
  assert_equal test_val, output[2]
118
-
203
+
119
204
  env = Rack::MockRequest.env_for('/foo_bar/133/maps/second', {
120
205
  :method => 'GET'
121
206
  })
@@ -125,7 +210,7 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
125
210
  end
126
211
  test_val = '0'
127
212
  assert_equal test_val, output[2]
128
-
213
+
129
214
  env = Rack::MockRequest.env_for('/foo_bar/133/maps/third', {
130
215
  :method => 'GET'
131
216
  })
@@ -136,10 +221,10 @@ class SampleApp::TestControllerModifiers < Test::Unit::TestCase
136
221
  test_val = '0'
137
222
  assert_equal test_val, output[2]
138
223
  end
139
-
224
+
140
225
  def test_keyed_with_type
141
226
  # baza controller exercises this option
142
- env = Rack::MockRequest.env_for('/foo_bar/133/children/1', {
227
+ env = Rack::MockRequest.env_for('/foo_bar/133/children/0', {
143
228
  :method => 'GET'
144
229
  })
145
230
  output = ''