restrack 1.7.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +9 -8
- data/README.markdown.html +25 -10
- data/Rakefile +5 -1
- data/bin/restrack +7 -4
- data/lib/restrack.rb +0 -5
- data/lib/restrack/async_web_service.rb +30 -0
- data/lib/restrack/generator.rb +24 -2
- data/lib/restrack/generator/Gemfile.erb +4 -0
- data/lib/restrack/generator/constants.yaml.erb +31 -0
- data/lib/restrack/generator/loader.rb.erb +1 -1
- data/lib/restrack/resource_request.rb +10 -7
- data/lib/restrack/version.rb +1 -1
- data/test/sample_app_1/config/constants.yaml +3 -0
- data/test/sample_app_1/test/test_controller_inputs.rb +17 -3
- metadata +5 -3
data/README.markdown
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RESTRack
|
2
2
|
- serving JSON and XML with REST and pleasure.
|
3
3
|
|
4
|
-
## Description
|
4
|
+
## Description
|
5
5
|
RESTRack is a [Rack](http://rack.rubyforge.org/)-based [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller)
|
6
6
|
framework that makes it extremely easy to develop [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer)ful
|
7
7
|
data services. It is inspired by [Rails](http://rubyonrails.org), and follows a few of its conventions. But it has no routes
|
@@ -17,12 +17,8 @@ view directory grouped by controller name subdirectories \(`view/<controller>/<a
|
|
17
17
|
requests will then render the view template with the builder gem, rather than generating XML with XmlSimple.
|
18
18
|
|
19
19
|
|
20
|
-
##
|
21
|
-
###
|
22
|
-
<sudo> gem install restrack
|
23
|
-
|
24
|
-
|
25
|
-
## Why RESTRack when there is Rails?
|
20
|
+
## Rationale
|
21
|
+
### Why RESTRack when there is Rails?
|
26
22
|
[Rails](http://rubyonrails.org/) is a powerful tool for full web applications. RESTRack is targeted at making
|
27
23
|
development of lightweight data services as easy as possible, while still giving you a performant and extensible
|
28
24
|
framework. The primary goal of of the development of RESTRack was to add as little as possible to the framework to give
|
@@ -32,10 +28,15 @@ Rails 3 instantiates approximately 80K more objects than RESTRack to do a hello
|
|
32
28
|
the default setup. Trimming Rails down by eliminating ActiveRecord, ActionMailer, and ActiveResource, it still
|
33
29
|
instantiates over 47K more objects than RESTRack.
|
34
30
|
|
35
|
-
|
31
|
+
### OK, so why RESTRack when there is Sinatra?
|
36
32
|
RESTRack provides a full, albeit small, framework for developing RESTful MVC applications.
|
37
33
|
|
38
34
|
|
35
|
+
## Installation
|
36
|
+
### Using [RubyGems](http://rubygems.org):
|
37
|
+
<sudo> gem install restrack
|
38
|
+
|
39
|
+
|
39
40
|
## CLI Usage:
|
40
41
|
### Generate a new service \(FooBar::WebService\)
|
41
42
|
- restrack generate service foo\_bar
|
data/README.markdown.html
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
<!doctype html>
|
3
|
+
<html lang="fr">
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<title>RESTRack</title>
|
7
|
+
<link rel="stylesheet" href="http://covertprestige.info/public/css/all.css" />
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<h1>RESTRack</h1>
|
2
11
|
|
3
12
|
<ul>
|
4
13
|
<li>serving JSON and XML with REST and pleasure.</li>
|
5
14
|
</ul>
|
6
15
|
|
7
|
-
<h2>Description
|
16
|
+
<h2>Description</h2>
|
8
17
|
|
9
18
|
<p>RESTRack is a <a href="http://rack.rubyforge.org/">Rack</a>-based <a href="http://en.wikipedia.org/wiki/Model%E2%80%93View%E2%80%93Controller">MVC</a>
|
10
19
|
framework that makes it extremely easy to develop <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a>ful
|
@@ -20,14 +29,9 @@ file, routing relationships are done through supplying custom code blocks to cla
|
|
20
29
|
view directory grouped by controller name subdirectories (<code>view/<controller>/<action>.xml.builder</code>). XML format
|
21
30
|
requests will then render the view template with the builder gem, rather than generating XML with XmlSimple.</p>
|
22
31
|
|
23
|
-
<h2>
|
32
|
+
<h2>Rationale</h2>
|
24
33
|
|
25
|
-
<h3>
|
26
|
-
|
27
|
-
<pre><code><sudo> gem install restrack
|
28
|
-
</code></pre>
|
29
|
-
|
30
|
-
<h2>Why RESTRack when there is Rails?</h2>
|
34
|
+
<h3>Why RESTRack when there is Rails?</h3>
|
31
35
|
|
32
36
|
<p><a href="http://rubyonrails.org/">Rails</a> is a powerful tool for full web applications. RESTRack is targeted at making
|
33
37
|
development of lightweight data services as easy as possible, while still giving you a performant and extensible
|
@@ -38,10 +42,17 @@ the web developer a good application space for developing JSON and XML services.
|
|
38
42
|
the default setup. Trimming Rails down by eliminating ActiveRecord, ActionMailer, and ActiveResource, it still
|
39
43
|
instantiates over 47K more objects than RESTRack.</p>
|
40
44
|
|
41
|
-
<
|
45
|
+
<h3>OK, so why RESTRack when there is Sinatra?</h3>
|
42
46
|
|
43
47
|
<p>RESTRack provides a full, albeit small, framework for developing RESTful MVC applications.</p>
|
44
48
|
|
49
|
+
<h2>Installation</h2>
|
50
|
+
|
51
|
+
<h3>Using <a href="http://rubygems.org">RubyGems</a>:</h3>
|
52
|
+
|
53
|
+
<pre><code><sudo> gem install restrack
|
54
|
+
</code></pre>
|
55
|
+
|
45
56
|
<h2>CLI Usage:</h2>
|
46
57
|
|
47
58
|
<h3>Generate a new service (FooBar::WebService)</h3>
|
@@ -345,3 +356,7 @@ Software.</p>
|
|
345
356
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
346
357
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
347
358
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
359
|
+
|
360
|
+
</body>
|
361
|
+
</html>
|
362
|
+
|
data/Rakefile
CHANGED
@@ -38,8 +38,12 @@ Rake::TestTask.new('test4') { |t|
|
|
38
38
|
t.pattern = 'test/sample_app_4/**/test_*.rb'
|
39
39
|
}
|
40
40
|
|
41
|
-
|
42
41
|
desc 'Run sample_app_5 tests.'
|
43
42
|
Rake::TestTask.new('test5') { |t|
|
44
43
|
t.pattern = 'test/sample_app_5/**/test_*.rb'
|
45
44
|
}
|
45
|
+
|
46
|
+
desc 'Run async app 1 tests.'
|
47
|
+
Rake::TestTask.new('asynctest1') { |t|
|
48
|
+
t.pattern = 'test/sample_app_async_1/**/test_*.rb'
|
49
|
+
}
|
data/bin/restrack
CHANGED
@@ -12,7 +12,10 @@ when :generate, :gen, :g
|
|
12
12
|
case noun.to_sym
|
13
13
|
when :service, :serv, :s
|
14
14
|
puts "Generating new RESTRack service #{name}..."
|
15
|
-
RESTRack::Generator.
|
15
|
+
RESTRack::Generator.generate_synchronous_service( name )
|
16
|
+
when :asynchronous_service, :asynch_service, :async_serv, :async, :as
|
17
|
+
puts "Generating new RESTRack service #{name}..."
|
18
|
+
RESTRack::Generator.generate_asynchronous_service( name )
|
16
19
|
when :controller, :cont, :c
|
17
20
|
predicate = ARGV[3] ? ARGV[3].to_sym : nil
|
18
21
|
case predicate
|
@@ -30,6 +33,6 @@ when :server, :s
|
|
30
33
|
options = { :Port => noun || 9292, :config => 'config.ru' }
|
31
34
|
options.merge({ :environment => ARGV[2] }) unless ARGV[2].nil?
|
32
35
|
Rack::Server.start( options )
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
when :version, :v
|
37
|
+
puts "Currently using RESTRack version " + RESTRack::VERSION.to_s + "."
|
38
|
+
end
|
data/lib/restrack.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module RESTRack
|
4
|
+
class AsyncWebService
|
5
|
+
AsyncResponse = [-1, {}, []].freeze
|
6
|
+
|
7
|
+
# Establish the namespace pointer.
|
8
|
+
def initialize
|
9
|
+
RESTRack::CONFIG[:SERVICE_NAME] = self.class.to_s.split('::')[0].to_sym
|
10
|
+
@request_hook = RESTRack::Hooks.new if RESTRack.const_defined?(:Hooks)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Handle requests in the Rack way.
|
14
|
+
def call( env )
|
15
|
+
EventMachine::defer do
|
16
|
+
resource_request = RESTRack::ResourceRequest.new( :request => Rack::Request.new(env) )
|
17
|
+
unless @request_hook.nil? or (RESTRack::CONFIG.has_key?(:PRE_PROCESSOR_DISABLED) and RESTRack::CONFIG[:PRE_PROCESSOR_DISABLED])
|
18
|
+
@request_hook.pre_processor(resource_request)
|
19
|
+
end
|
20
|
+
response = RESTRack::Response.new(resource_request)
|
21
|
+
unless @request_hook.nil? or (RESTRack::CONFIG.has_key?(:POST_PROCESSOR_DISABLED) and RESTRack::CONFIG[:POST_PROCESSOR_DISABLED])
|
22
|
+
@request_hook.post_processor(response)
|
23
|
+
end
|
24
|
+
env['async.callback'].call response.output
|
25
|
+
end
|
26
|
+
AsyncResponse
|
27
|
+
end # method call
|
28
|
+
|
29
|
+
end # class WebService
|
30
|
+
end # module RESTRack
|
data/lib/restrack/generator.rb
CHANGED
@@ -12,7 +12,8 @@ module RESTRack
|
|
12
12
|
:constants => 'constants.yaml.erb',
|
13
13
|
:controller => 'controller.rb.erb',
|
14
14
|
:descendant_controller => 'descendant_controller.rb.erb',
|
15
|
-
:hooks => 'hooks.rb.erb'
|
15
|
+
:hooks => 'hooks.rb.erb',
|
16
|
+
:gemfile => 'Gemfile.erb'
|
16
17
|
}
|
17
18
|
|
18
19
|
class << self
|
@@ -36,7 +37,9 @@ module RESTRack
|
|
36
37
|
FileUtils.makedirs("#{base_dir}/views/#{name}")
|
37
38
|
end
|
38
39
|
|
39
|
-
#
|
40
|
+
# The guts for generating a new RESTRack service. The loader.rb template
|
41
|
+
# will create a RESTRack::WebService or RESTRack::AsyncWebService based on
|
42
|
+
# the value of @async.
|
40
43
|
def generate_service(name)
|
41
44
|
FileUtils.makedirs("#{name}/config")
|
42
45
|
FileUtils.makedirs("#{name}/controllers")
|
@@ -59,6 +62,25 @@ module RESTRack
|
|
59
62
|
template = get_template_for( :hooks )
|
60
63
|
resultant_string = template.result( get_binding_for_service( name ) )
|
61
64
|
File.open("#{name}/hooks.rb", 'w') {|f| f.puts resultant_string }
|
65
|
+
|
66
|
+
# Added following lines to generate 'Gemfile' automatically.
|
67
|
+
template = get_template_for( :gemfile )
|
68
|
+
resultant_string = template.result( get_binding_for_service( name ) )
|
69
|
+
File.open("#{name}/Gemfile", 'w') {|f| f.puts resultant_string }
|
70
|
+
end
|
71
|
+
# Generate a new RESTRack service. This does not use EventMachine and can
|
72
|
+
# be used with any Rack supported web server.
|
73
|
+
def generate_synchronous_service(name)
|
74
|
+
@async = false
|
75
|
+
generate_service(name)
|
76
|
+
@async = nil
|
77
|
+
end
|
78
|
+
# Generate a new RESTRack service that uses EventMachine. This can only be
|
79
|
+
# used with web servers that use or support EventMachine (Thin, Rainbows).
|
80
|
+
def generate_asynchronous_service(name)
|
81
|
+
@async = true
|
82
|
+
generate_service(name)
|
83
|
+
@async = nil
|
62
84
|
end
|
63
85
|
|
64
86
|
private
|
@@ -22,3 +22,34 @@
|
|
22
22
|
:ROOT_RESOURCE_ACCEPT: []
|
23
23
|
# 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).
|
24
24
|
:ROOT_RESOURCE_DENY: []
|
25
|
+
|
26
|
+
# A list of input parameters which should not be output to logs:
|
27
|
+
#:PARAMS_NOT_LOGGABLE: [:password]
|
28
|
+
|
29
|
+
# :TRANSCODE: and :FORCE_ENCODING: are optional config settings
|
30
|
+
# String#encode will be called when this value is set
|
31
|
+
#:TRANSCODE: ISO-8859-1 #or UTF-8 etc
|
32
|
+
# String#force_encoding will be called when this value is set
|
33
|
+
#:FORCE_ENCODING: ISO-8859-1
|
34
|
+
|
35
|
+
# :CORS: is an optional config setting
|
36
|
+
# CORS Header configuration
|
37
|
+
# Supported:
|
38
|
+
# - Access-Control-Allow-Origin: http://localhost
|
39
|
+
# - Access-Control-Allow-Methods: POST, GET
|
40
|
+
# List of all:
|
41
|
+
# - Access-Control-Allow-Origin: <origin> | *
|
42
|
+
# e.g. Access-Control-Allow-Origin: http://mozilla.com
|
43
|
+
# - Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
|
44
|
+
# - Access-Control-Max-Age: <delta-seconds>
|
45
|
+
# - Access-Control-Allow-Credentials: true | false
|
46
|
+
# - Access-Control-Allow-Methods: <method>[, <method>]*
|
47
|
+
# e.g. Access-Control-Allow-Methods: POST, GET
|
48
|
+
# - Access-Control-Allow-Headers: <field-name>[, <field-name>]*
|
49
|
+
#:CORS:
|
50
|
+
# Access-Control-Allow-Origin: http://restrack.me
|
51
|
+
# Access-Control-Allow-Methods: POST, GET
|
52
|
+
|
53
|
+
# :PRE_PROCESSOR_DISABLED: and :POST_PROCESSOR_DISABLED: are optional config settings and are false by default
|
54
|
+
#:PRE_PROCESSOR_DISABLED: true
|
55
|
+
#:POST_PROCESSOR_DISABLED: true
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'restrack'
|
2
2
|
|
3
3
|
module <%= @service_name.camelize %>; end
|
4
|
-
class <%= @service_name.camelize %>::WebService < RESTRack
|
4
|
+
class <%= @service_name.camelize %>::WebService < RESTRack::<%= @async ? 'Async' : '' %>WebService; end
|
5
5
|
|
6
6
|
RESTRack::CONFIG = RESTRack::load_config(File.join(File.dirname(__FILE__), 'config/constants.yaml'))
|
7
7
|
RESTRack::CONFIG[:ROOT] = File.dirname(__FILE__)
|
@@ -15,9 +15,6 @@ module RESTRack
|
|
15
15
|
@request.env.select {|k,v| k.start_with? 'HTTP_'}.each do |k,v|
|
16
16
|
@headers[k.sub(/^HTTP_/, '')] = v
|
17
17
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def prepare
|
21
18
|
# MIME type should be determined before raising any exceptions for proper error reporting
|
22
19
|
# Set up the initial routing.
|
23
20
|
@url_chain = @request.path_info.split('/')
|
@@ -32,16 +29,17 @@ module RESTRack
|
|
32
29
|
end
|
33
30
|
# Determine MIME type from extension
|
34
31
|
@mime_type = get_mime_type_from( extension )
|
32
|
+
end
|
33
|
+
|
34
|
+
def prepare
|
35
35
|
# Now safe to raise exceptions
|
36
36
|
raise HTTP400BadRequest, "Request path of #{@request.path_info} is invalid" if @request.path_info.include?('//')
|
37
|
-
|
38
37
|
# For CORS support
|
39
38
|
if RESTRack::CONFIG[:CORS]
|
40
39
|
raise HTTP403Forbidden if @headers['Origin'].nil?
|
41
40
|
raise HTTP403Forbidden unless RESTRack::CONFIG[:CORS]['Access-Control-Allow-Origin'] == '*' or RESTRack::CONFIG[:CORS]['Access-Control-Allow-Origin'].include?(@headers['Origin'])
|
42
41
|
raise HTTP403Forbidden unless @request.env['REQUEST_METHOD'] == 'OPTIONS' or RESTRack::CONFIG[:CORS]['Access-Control-Allow-Methods'] == '*' or RESTRack::CONFIG[:CORS]['Access-Control-Allow-Methods'].include?(@request.env['REQUEST_METHOD'])
|
43
42
|
end
|
44
|
-
|
45
43
|
# Pull input data from POST body
|
46
44
|
@post_params = parse_body( @request )
|
47
45
|
@get_params = parse_query_string( @request )
|
@@ -53,7 +51,6 @@ module RESTRack
|
|
53
51
|
end
|
54
52
|
@params.symbolize!
|
55
53
|
log_request_params(@params)
|
56
|
-
|
57
54
|
# Pull first controller from URL
|
58
55
|
@active_resource_name = @url_chain.shift
|
59
56
|
unless @active_resource_name.nil? or RESTRack.controller_exists?(@active_resource_name)
|
@@ -69,7 +66,13 @@ module RESTRack
|
|
69
66
|
end
|
70
67
|
|
71
68
|
def log_request_params(params_hash)
|
72
|
-
|
69
|
+
params_to_log = params_hash.clone
|
70
|
+
if RESTRack::CONFIG[:PARAMS_NOT_LOGGABLE]
|
71
|
+
params_to_log.each_key do |param|
|
72
|
+
params_to_log[param] = '*****' if RESTRack::CONFIG[:PARAMS_NOT_LOGGABLE].include?(param.to_s)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
RESTRack.request_log.debug 'Combined Request Params: ' + params_to_log.inspect
|
73
76
|
end
|
74
77
|
|
75
78
|
# Call the next entity in the path stack.
|
data/lib/restrack/version.rb
CHANGED
@@ -27,6 +27,9 @@
|
|
27
27
|
# The stack trace will not be added to 500 response body by default, set to true to enable.
|
28
28
|
:SHOW_STACK: true
|
29
29
|
|
30
|
+
# A list of input parameters which should not be output to logs:
|
31
|
+
:PARAMS_NOT_LOGGABLE: [ password ]
|
32
|
+
|
30
33
|
# :TRANSCODE: and :FORCE_ENCODING: are optional config settings
|
31
34
|
# String#encode will be called when this value is set
|
32
35
|
#:TRANSCODE: ISO-8859-1 #or UTF-8 etc
|
@@ -21,7 +21,7 @@ class SampleApp::TestControllerInputs < Test::Unit::TestCase
|
|
21
21
|
test_val = { :test => '1', :hello => 'world', 'get?' => 'true' }.to_json
|
22
22
|
assert_equal test_val, output[2][0]
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def test_FUBAR_params
|
26
26
|
env = Rack::MockRequest.env_for('/foo_bar/echo_get?test=1&hello=world', {
|
27
27
|
:method => 'DELETE'
|
@@ -60,7 +60,7 @@ class SampleApp::TestControllerInputs < Test::Unit::TestCase
|
|
60
60
|
end
|
61
61
|
assert_equal test_val, output[2][0]
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def test_post_xml
|
65
65
|
test_val = XmlSimple.xml_out({ :echo => 'niner' }, 'AttrPrefix' => true, 'XmlDeclaration' => true, 'NoIndent' => true)
|
66
66
|
env = Rack::MockRequest.env_for('/foo_bar/echo.xml', {
|
@@ -74,7 +74,7 @@ class SampleApp::TestControllerInputs < Test::Unit::TestCase
|
|
74
74
|
end
|
75
75
|
assert_equal test_val, output[2][0]
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
def test_post_text
|
79
79
|
test_val = 'OPCODE=PEBKAC'
|
80
80
|
env = Rack::MockRequest.env_for('/foo_bar/echo.txt', {
|
@@ -88,4 +88,18 @@ class SampleApp::TestControllerInputs < Test::Unit::TestCase
|
|
88
88
|
end
|
89
89
|
assert_equal test_val, output[2][0]
|
90
90
|
end
|
91
|
+
|
92
|
+
def test_post_password_param_no_log
|
93
|
+
test_val = { :echo => 'niner', :password => 'my_password' }.to_json
|
94
|
+
env = Rack::MockRequest.env_for('/foo_bar/echo', {
|
95
|
+
:method => 'POST',
|
96
|
+
:input => test_val,
|
97
|
+
'CONTENT_TYPE' => 'application/json'
|
98
|
+
})
|
99
|
+
output = ''
|
100
|
+
assert_nothing_raised do
|
101
|
+
output = @ws.call(env)
|
102
|
+
end
|
103
|
+
assert_equal test_val, output[2][0]
|
104
|
+
end
|
91
105
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restrack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -164,7 +164,9 @@ files:
|
|
164
164
|
- Rakefile
|
165
165
|
- bin/restrack
|
166
166
|
- lib/restrack.rb
|
167
|
+
- lib/restrack/async_web_service.rb
|
167
168
|
- lib/restrack/generator.rb
|
169
|
+
- lib/restrack/generator/Gemfile.erb
|
168
170
|
- lib/restrack/generator/config.ru.erb
|
169
171
|
- lib/restrack/generator/constants.yaml.erb
|
170
172
|
- lib/restrack/generator/controller.rb.erb
|
@@ -255,7 +257,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
255
257
|
version: '0'
|
256
258
|
requirements: []
|
257
259
|
rubyforge_project: restrack
|
258
|
-
rubygems_version: 1.8.
|
260
|
+
rubygems_version: 1.8.24
|
259
261
|
signing_key:
|
260
262
|
specification_version: 3
|
261
263
|
summary: A lightweight MVC framework developed specifically for JSON (and XML) REST
|