restrack 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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 targetted at
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
- Rake::TestTask.new('test_all') { |t|
11
- t.pattern = 'test/**/test_*.rb'
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.'
@@ -0,0 +1,3 @@
1
+ # Rails.root/config.ru
2
+ require 'loader'
3
+ run <%= @service_name.camelize %>::WebService.new
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module RESTRack
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -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
@@ -16,7 +16,6 @@ end
16
16
  # Dynamically load all files in lib
17
17
  Find.find( File.join(File.dirname(__FILE__)) ) do |file|
18
18
  next if File.extname(file) != '.rb'
19
- puts 'loading file ' + file
20
19
  require file
21
20
  end
22
21
 
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: 25
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
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
@@ -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).