restrack 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = ''