restrack 0.0.3 → 0.0.5
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.
- data/README.rdoc +2 -1
- data/Rakefile +9 -2
- data/lib/restrack/generator/config.ru.erb +3 -0
- data/lib/restrack/generator.rb +7 -1
- data/lib/restrack/resource_controller.rb +35 -32
- data/lib/restrack/resource_request.rb +9 -8
- data/lib/restrack/support.rb +4 -1
- data/lib/restrack/version.rb +1 -1
- data/lib/restrack/web_service.rb +4 -4
- data/lib/restrack.rb +0 -1
- data/test/test_support.rb +5 -1
- metadata +4 -4
- data/config/constants.yaml +0 -8
data/README.rdoc
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
|
23
23
|
|
24
24
|
== Why RESTRack when there is Rails?
|
25
|
-
Rails is a powerful tool for full web applications. RESTRack is
|
25
|
+
Rails is a powerful tool for full web applications. RESTRack is targeted at
|
26
26
|
making development of lightweight data services as easy as possible, while
|
27
27
|
still giving you a performant and extensible framework. The primary goal of
|
28
28
|
the framework was to add as little as possible to the framework to give the
|
@@ -47,6 +47,7 @@
|
|
47
47
|
== How-Tos
|
48
48
|
|
49
49
|
...yet to be done...
|
50
|
+
=== Authentication Suggestions
|
50
51
|
|
51
52
|
=== Logging/Logging Level
|
52
53
|
|
data/Rakefile
CHANGED
@@ -7,8 +7,15 @@ Bundler::GemHelper.install_tasks
|
|
7
7
|
task :default => [:test_all]
|
8
8
|
|
9
9
|
desc 'Run all tests.'
|
10
|
-
|
11
|
-
|
10
|
+
task :test_all do
|
11
|
+
for n in 0..4
|
12
|
+
Rake::Task['test'+n.to_s].invoke
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Run base tests.'
|
17
|
+
Rake::TestTask.new('test0') { |t|
|
18
|
+
t.pattern = 'test/test_*.rb'
|
12
19
|
}
|
13
20
|
|
14
21
|
desc 'Run sample_app_1 tests.'
|
data/lib/restrack/generator.rb
CHANGED
@@ -7,14 +7,15 @@ module RESTRack
|
|
7
7
|
class Generator
|
8
8
|
TEMPLATE = {
|
9
9
|
:service => 'loader.rb.erb',
|
10
|
+
:rackup => 'config.ru.erb',
|
10
11
|
:constants => 'constants.yaml.erb',
|
11
12
|
:controller => 'controller.rb.erb'
|
12
13
|
}
|
13
14
|
|
14
15
|
class << self
|
15
16
|
|
17
|
+
# Generate controller file
|
16
18
|
def generate_controller(name)
|
17
|
-
# Generate controller file
|
18
19
|
template = get_template_for( :controller )
|
19
20
|
resultant_string = template.result( get_binding_for_controller( name ) )
|
20
21
|
File.open("#{base_dir}/controllers/#{name}_controller.rb", 'w') {|f| f.puts resultant_string }
|
@@ -22,6 +23,7 @@ module RESTRack
|
|
22
23
|
FileUtils.makedirs("#{name}/views")
|
23
24
|
end
|
24
25
|
|
26
|
+
# Generate a new RESTRack service
|
25
27
|
def generate_service(name)
|
26
28
|
FileUtils.makedirs("#{name}/config")
|
27
29
|
FileUtils.makedirs("#{name}/controllers")
|
@@ -33,6 +35,10 @@ module RESTRack
|
|
33
35
|
resultant_string = template.result( get_binding_for_service( name ) )
|
34
36
|
File.open("#{name}/loader.rb", 'w') {|f| f.puts resultant_string }
|
35
37
|
|
38
|
+
template = get_template_for( :rackup )
|
39
|
+
resultant_string = template.result( get_binding_for_service( name ) )
|
40
|
+
File.open("#{name}/config.ru", 'w') {|f| f.puts resultant_string }
|
41
|
+
|
36
42
|
template = get_template_for( :constants )
|
37
43
|
resultant_string = template.result( get_binding_for_service( name ) )
|
38
44
|
File.open("#{name}/config/constants.yaml", 'w') {|f| f.puts resultant_string }
|
@@ -1,10 +1,25 @@
|
|
1
1
|
module RESTRack
|
2
|
+
# All RESTRack controllers descend from ResourceController.
|
3
|
+
|
4
|
+
# HTTP Verb: | GET | PUT | POST | DELETE
|
5
|
+
# Collection URI (/widgets/): | index | replace | create | drop
|
6
|
+
# Element URI (/widgets/42): | show | update | add | destroy
|
7
|
+
|
8
|
+
#def index; end
|
9
|
+
#def replace; end
|
10
|
+
#def create; end
|
11
|
+
#def drop; end
|
12
|
+
#def show(id); end
|
13
|
+
#def update(id); end
|
14
|
+
#def add(id); end
|
15
|
+
#def destroy(id); end
|
16
|
+
|
2
17
|
class ResourceController
|
3
18
|
attr_reader :input, :output
|
4
19
|
|
20
|
+
# Base initialization method for resources and storage of request input
|
21
|
+
# This method should not be overriden in decendent classes.
|
5
22
|
def self.__init(resource_request)
|
6
|
-
# Base initialization method for resources and storage of request input
|
7
|
-
# This method should not be overriden in decendent classes.
|
8
23
|
__self = self.new
|
9
24
|
return __self.__init(resource_request)
|
10
25
|
end
|
@@ -14,33 +29,20 @@ module RESTRack
|
|
14
29
|
self
|
15
30
|
end
|
16
31
|
|
32
|
+
# Call the controller's action and return it in the proper format.
|
17
33
|
def call
|
18
|
-
# Call the controller's action and return it in the proper format.
|
19
34
|
args = []
|
20
35
|
args << @resource_request.id unless @resource_request.id.blank?
|
21
36
|
package( self.send(@resource_request.action.to_sym, *args) )
|
22
37
|
end
|
23
38
|
|
24
|
-
# HTTP Verb: | GET | PUT | POST | DELETE
|
25
|
-
# Collection URI (/widgets/): | index | replace | create | drop
|
26
|
-
# Element URI (/widgets/42): | show | update | add | destroy
|
27
|
-
|
28
|
-
#def index; end
|
29
|
-
#def replace; end
|
30
|
-
#def create; end
|
31
|
-
#def drop; end
|
32
|
-
#def show(id); end
|
33
|
-
#def update(id); end
|
34
|
-
#def add(id); end
|
35
|
-
#def destroy(id); end
|
36
|
-
|
37
39
|
def method_missing(method_sym, *arguments, &block)
|
38
40
|
raise HTTP405MethodNotAllowed, 'Method not provided on controller.'
|
39
41
|
end
|
40
42
|
|
41
43
|
protected # all internal methods are protected rather than private so that calling methods *could* be overriden if necessary.
|
44
|
+
# This method allows one to access a related resource, without providing a direct link to specific relation(s).
|
42
45
|
def self.has_relationship_to(entity, opts = {})
|
43
|
-
# This method allows one to access a related resource, without providing a direct link to specific relation(s).
|
44
46
|
entity_name = opts[:as] || entity
|
45
47
|
define_method( entity_name.to_sym,
|
46
48
|
Proc.new do
|
@@ -56,11 +58,11 @@ module RESTRack
|
|
56
58
|
)
|
57
59
|
end
|
58
60
|
|
61
|
+
# This method defines that there is a single link to a member from an entity collection.
|
62
|
+
# The second parameter is an options hash to support setting the local name of the relation via ':as => :foo'.
|
63
|
+
# The third parameter to the method is a Proc which accepts the calling entity's id and returns the id of the relation to which we're establishing the link.
|
64
|
+
# This adds an accessor instance method whose name is the entity's class.
|
59
65
|
def self.has_direct_relationship_to(entity, opts = {}, &get_entity_id_from_relation_id)
|
60
|
-
# This method defines that there is a single link to a member from an entity collection.
|
61
|
-
# The second parameter is an options hash to support setting the local name of the relation via ':as => :foo'.
|
62
|
-
# The third parameter to the method is a Proc which accepts the calling entity's id and returns the id of the relation to which we're establishing the link.
|
63
|
-
# This adds an accessor instance method whose name is the entity's class.
|
64
66
|
entity_name = opts[:as] || entity
|
65
67
|
define_method( entity_name.to_sym,
|
66
68
|
Proc.new do
|
@@ -73,9 +75,9 @@ module RESTRack
|
|
73
75
|
)
|
74
76
|
end
|
75
77
|
|
78
|
+
# This method defines that there are multiple links to members from an entity collection (an array of entity identifiers).
|
79
|
+
# This adds an accessor instance method whose name is the entity's class.
|
76
80
|
def self.has_direct_relationships_to(entity, opts = {}, &get_entity_id_from_relation_id)
|
77
|
-
# This method defines that there are multiple links to members from an entity collection (an array of entity identifiers).
|
78
|
-
# This adds an accessor instance method whose name is the entity's class.
|
79
81
|
entity_name = opts[:as] || entity
|
80
82
|
define_method( entity_name.to_sym,
|
81
83
|
Proc.new do
|
@@ -91,9 +93,9 @@ module RESTRack
|
|
91
93
|
)
|
92
94
|
end
|
93
95
|
|
96
|
+
# This method defines that there are mapped links to members from an entity collection (a hash of entity identifiers).
|
97
|
+
# This adds an accessor instance method whose name is the entity's class.
|
94
98
|
def self.has_mapped_relationships_to(entity, opts = {}, &get_entity_id_from_relation_id)
|
95
|
-
# This method defines that there are mapped links to members from an entity collection (a hash of entity identifiers).
|
96
|
-
# This adds an accessor instance method whose name is the entity's class.
|
97
99
|
entity_name = opts[:as] || entity
|
98
100
|
define_method( entity_name.to_sym,
|
99
101
|
Proc.new do
|
@@ -109,17 +111,17 @@ module RESTRack
|
|
109
111
|
)
|
110
112
|
end
|
111
113
|
|
114
|
+
# Call the child relation (next entity in the path stack)
|
115
|
+
# common logic to all relationship methods
|
112
116
|
def call_relation(entity)
|
113
|
-
# Call the child relation (next entity in the path stack)
|
114
|
-
# common logic to all relationship methods
|
115
117
|
@resource_request.resource_name = entity.to_s.camelize
|
116
118
|
setup_action
|
117
119
|
@resource_request.locate
|
118
120
|
@resource_request.call
|
119
121
|
end
|
120
122
|
|
123
|
+
# If the action is not set with the request URI, determine the action from HTTP Verb.
|
121
124
|
def setup_action
|
122
|
-
# If the action is not set with the request URI, determine the action from HTTP Verb.
|
123
125
|
if @resource_request.action.blank?
|
124
126
|
if @resource_request.request.get?
|
125
127
|
@resource_request.action = @resource_request.id.blank? ? :index : :show
|
@@ -135,13 +137,13 @@ module RESTRack
|
|
135
137
|
end
|
136
138
|
end
|
137
139
|
|
140
|
+
# Allows decendent controllers to set a data type for the id other than the default.
|
138
141
|
def self.keyed_with_type(klass)
|
139
|
-
# Allows decendent controllers to set a data type for the id other than the default.
|
140
142
|
@@key_type = klass
|
141
143
|
end
|
142
144
|
|
145
|
+
# This method is used to convert the id coming off of the path stack, which is in string form, into another data type if one has been set.
|
143
146
|
def format_id
|
144
|
-
# This method is used to convert the id coming off of the path stack, which is in string form, into another data type if one has been set.
|
145
147
|
@@key_type ||= nil
|
146
148
|
unless @@key_type.blank?
|
147
149
|
if @@key_type == Fixnum
|
@@ -156,8 +158,8 @@ module RESTRack
|
|
156
158
|
end
|
157
159
|
end
|
158
160
|
|
161
|
+
# This handles outputing properly formatted content based on the file extension in the URL.
|
159
162
|
def package(data)
|
160
|
-
# This handles outputing properly formatted content based on the file extension in the URL.
|
161
163
|
if @resource_request.mime_type.like?( RESTRack.mime_type_for( :JSON ) )
|
162
164
|
@output = data.to_json
|
163
165
|
elsif @resource_request.mime_type.like?( RESTRack.mime_type_for( :XML ) )
|
@@ -175,8 +177,8 @@ module RESTRack
|
|
175
177
|
end
|
176
178
|
end
|
177
179
|
|
180
|
+
# Use Builder to generate the XML.
|
178
181
|
def builder_up(data)
|
179
|
-
# Use Builder to generate the XML
|
180
182
|
buffer = ''
|
181
183
|
xml = Builder::XmlMarkup.new(:target => buffer)
|
182
184
|
xml.instruct!
|
@@ -184,6 +186,7 @@ module RESTRack
|
|
184
186
|
return buffer
|
185
187
|
end
|
186
188
|
|
189
|
+
# Builds the path to the builder file for the current controller action.
|
187
190
|
def builder_file
|
188
191
|
"#{RESTRack::CONFIG[:ROOT]}/views/#{@resource_request.resource_name.underscore}/#{@resource_request.action}.xml.builder"
|
189
192
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module RESTRack
|
2
|
+
# The ResourceRequest class handles all incoming requests.
|
2
3
|
class ResourceRequest
|
3
4
|
attr_reader :request, :request_id, :input
|
4
5
|
attr_accessor :mime_type, :path_stack, :resource_name, :action, :id
|
5
6
|
|
7
|
+
# Initialize the ResourceRequest by assigning a request_id and determining the path, format, and controller of the resource.
|
8
|
+
# Accepting options to allow us to override request_id for testing.
|
6
9
|
def initialize(opts)
|
7
|
-
# Initialize the ResourceRequest by assigning a request_id and determining the path, format, and controller of the resource.
|
8
|
-
# Accepting options just to allow us to override request_id for testing.
|
9
10
|
@request = opts[:request]
|
10
11
|
@request_id = opts[:request_id] || get_request_id
|
11
12
|
|
@@ -28,21 +29,21 @@ module RESTRack
|
|
28
29
|
raise HTTP403Forbidden if not RESTRack::CONFIG[:ROOT_RESOURCE_DENY].blank? and RESTRack::CONFIG[:ROOT_RESOURCE_DENY].include?(@resource_name)
|
29
30
|
end
|
30
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.
|
31
34
|
def locate
|
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
35
|
RESTRack.log.debug "{#{@request_id}} Locating Resource"
|
35
36
|
@resource = instantiate_controller
|
36
37
|
end
|
37
38
|
|
39
|
+
# Pass along the `call` method to the typed resource object, this must occur after a call to locate.
|
38
40
|
def call
|
39
|
-
# Pass along the `call` method to the typed resource object, this must occur after a call to locate.
|
40
41
|
RESTRack.log.debug "{#{@request_id}} Processing Request"
|
41
42
|
@resource.call
|
42
43
|
end
|
43
44
|
|
45
|
+
# Send out the typed resource's output, this must occur after a call to run.
|
44
46
|
def output
|
45
|
-
# Send out the typed resource's output, this must occur after a call to run.
|
46
47
|
RESTRack.log.debug "{#{@request_id}} Retrieving Output"
|
47
48
|
@resource.output
|
48
49
|
end
|
@@ -77,8 +78,8 @@ module RESTRack
|
|
77
78
|
input
|
78
79
|
end
|
79
80
|
|
81
|
+
# Remove the extension from the URL if present, that will be used to determine content-type.
|
80
82
|
def split_extension_from(path_stack)
|
81
|
-
# Remove the extension from the URL if present, that will be used to determine content-type.
|
82
83
|
extension = ''
|
83
84
|
unless path_stack.nil?
|
84
85
|
path_stack = path_stack.sub(/\.([^.]*)$/) do |s|
|
@@ -122,8 +123,8 @@ module RESTRack
|
|
122
123
|
[id, action, path_stack]
|
123
124
|
end
|
124
125
|
|
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.
|
125
127
|
def instantiate_controller
|
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
128
|
begin
|
128
129
|
return RESTRack.controller_class_for( @resource_name ).__init(self)
|
129
130
|
rescue
|
data/lib/restrack/support.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
module RESTRack
|
2
|
+
require 'mime/types'
|
3
|
+
require 'yaml'
|
4
|
+
require 'logger'
|
2
5
|
|
3
6
|
class << self
|
4
7
|
def log; @@log; end
|
@@ -44,8 +47,8 @@ module RESTRack
|
|
44
47
|
|
45
48
|
end
|
46
49
|
|
47
|
-
# Courtesy of Rails' ActiveSupport, thank you DHH et al.
|
48
50
|
class Object
|
51
|
+
# Courtesy of Rails' ActiveSupport, thank you DHH et al.
|
49
52
|
def blank?
|
50
53
|
respond_to?(:empty?) ? empty? : !self
|
51
54
|
end
|
data/lib/restrack/version.rb
CHANGED
data/lib/restrack/web_service.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module RESTRack
|
2
2
|
class WebService
|
3
3
|
|
4
|
+
# Establish the namespace pointer.
|
4
5
|
def initialize
|
5
|
-
# Establish the namespace pointer.
|
6
6
|
RESTRack::CONFIG[:SERVICE_NAME] = self.class.to_s.split('::')[0].to_sym
|
7
7
|
end
|
8
8
|
|
9
|
+
# Handle requests in the Rack way.
|
9
10
|
def call( env )
|
10
|
-
# Handle requests.
|
11
11
|
request = Rack::Request.new(env)
|
12
12
|
begin
|
13
13
|
resource_request = RESTRack::ResourceRequest.new( :request => request )
|
@@ -22,15 +22,15 @@ module RESTRack
|
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
|
+
# Return HTTP200OK SUCCESS
|
25
26
|
def valid( resource_request, response )
|
26
|
-
# Return HTTP200OK SUCCESS
|
27
27
|
RESTRack.request_log.debug "'#{resource_request.mime_type.to_s}' response data (Request ID: #{resource_request.request_id})\n" + response.to_s unless not response.respond_to?( :to_s )
|
28
28
|
RESTRack.request_log.info "HTTP200OK - (Request ID: #{resource_request.request_id})"
|
29
29
|
return [200, {'Content-Type' => resource_request.content_type}, response ]
|
30
30
|
end
|
31
31
|
|
32
|
+
# Return appropriate response code and messages per raised exception type.
|
32
33
|
def caught( resource_request, exception )
|
33
|
-
# Return appropriate response code and messages per raised exception type.
|
34
34
|
if resource_request && resource_request.request_id
|
35
35
|
RESTRack.request_log.info exception.message + "(Request ID: #{resource_request.request_id})"
|
36
36
|
else
|
data/lib/restrack.rb
CHANGED
data/test/test_support.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
|
-
require File.expand_path(File.join(File.dirname(__FILE__),'../lib/restrack'))
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'../lib/restrack/support'))
|
4
4
|
|
5
5
|
module RESTRack
|
6
6
|
class TestSupport < Test::Unit::TestCase
|
7
7
|
|
8
|
+
RESTRack::CONFIG = RESTRack.load_config(File.expand_path(File.join(File.dirname(__FILE__),'../test/sample_app_1/config/constants.yaml')))
|
9
|
+
|
8
10
|
def test_constants
|
9
11
|
assert_nothing_raised do
|
10
12
|
RESTRack::CONFIG[:LOG].to_sym.to_s
|
13
|
+
RESTRack::CONFIG[:LOG_LEVEL].to_sym.to_s
|
11
14
|
RESTRack::CONFIG[:REQUEST_LOG].to_sym.to_s
|
15
|
+
RESTRack::CONFIG[:REQUEST_LOG_LEVEL].to_sym.to_s
|
12
16
|
RESTRack::CONFIG[:DEFAULT_FORMAT].to_sym.to_s
|
13
17
|
RESTRack::CONFIG[:DEFAULT_RESOURCE].to_sym.to_s
|
14
18
|
assert RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].blank? || RESTRack::CONFIG[:ROOT_RESOURCE_ACCEPT].class == Array
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restrack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris St. John
|
@@ -118,9 +118,9 @@ files:
|
|
118
118
|
- README.rdoc
|
119
119
|
- Rakefile
|
120
120
|
- bin/restrack
|
121
|
-
- config/constants.yaml
|
122
121
|
- lib/restrack.rb
|
123
122
|
- lib/restrack/generator.rb
|
123
|
+
- lib/restrack/generator/config.ru.erb
|
124
124
|
- lib/restrack/generator/constants.yaml.erb
|
125
125
|
- lib/restrack/generator/controller.rb.erb
|
126
126
|
- lib/restrack/generator/loader.rb.erb
|
data/config/constants.yaml
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
:LOG: '/var/log/restrack/restrack.log'
|
2
|
-
:REQUEST_LOG: '/var/log/restrack/restrack.request.log'
|
3
|
-
:LOG_LEVEL: DEBUG # Logger object level
|
4
|
-
:REQUEST_LOG_LEVEL: DEBUG # Logger object level
|
5
|
-
:DEFAULT_FORMAT: :JSON # Supported formats are :JSON, :XML, :YAML, :BIN, :TEXT
|
6
|
-
:DEFAULT_RESOURCE: '' # The resource which will handle root level requests where the name is not specified. Best for users of this not to implement method_missing in their default controller, unless they are checking for bad URI. :DEFAULT_RESOURCE should be a member of :ROOT_RESOURCE_ACCEPT.
|
7
|
-
:ROOT_RESOURCE_ACCEPT: [] # These are the resources which can be accessed from the root of your web service. If left empty, all resources are available at the root.
|
8
|
-
:ROOT_RESOURCE_DENY: [] # These are the resources which cannot be accessed from the root of your web service. Use either this or ROOT_RESOURCE_ACCEPT as a blacklist or whitelist to establish routing (relationships defined in resource controllers define further routing).
|