rails-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,228 @@
1
+ # Rails::API
2
+
3
+ **Rails::API** is a subset of a normal Rails application, created for applications that don't require all functionality that a complete Rails application provides. It is a bit more lightweight, and consequently a bit faster than a normal Rails application. The main example for its usage is in API applications only, where you usually don't need the entire Rails middleware stack nor template generation.
4
+
5
+ ## Using Rails for API-only Apps
6
+
7
+ This is a quick walk-through to help you get up and running with **Rails::API** to create API-only Apps, covering:
8
+
9
+ * What **Rails::API** provides for API-only applications
10
+ * How to decide which middlewares you will want to include
11
+ * How to decide which modules to use in your controller
12
+
13
+ ### What is an API app?
14
+
15
+ Traditionally, when people said that they used Rails as an "API", they meant providing a programmatically accessible API alongside their web application.
16
+ For example, GitHub provides [an API](http://developer.github.com) that you can use from your own custom clients.
17
+
18
+ With the advent of client-side frameworks, more developers are using Rails to build a backend that is shared between their web application and other native applications.
19
+
20
+ For example, Twitter uses its [public API](https://dev.twitter.com) in its web application, which is built as a static site that consumes JSON resources.
21
+
22
+ Instead of using Rails to generate dynamic HTML that will communicate with the server through forms and links, many developers are treating their web application as just another client, consuming a simple JSON API.
23
+
24
+ This guide covers building a Rails application that serves JSON resources to an API client *or* client-side framework.
25
+
26
+ ### Why use Rails for JSON APIs?
27
+
28
+ The first question a lot of people have when thinking about building a JSON API using Rails is: "isn't using Rails to spit out some JSON overkill? Shouldn't I just use something like Sinatra?"
29
+
30
+ For very simple APIs, this may be true. However, even in very HTML-heavy applications, most of an application's logic is actually outside of the view layer.
31
+
32
+ The reason most people use Rails is that it provides a set of defaults that allows us to get up and running quickly without having to make a lot of trivial decisions.
33
+
34
+ Let's take a look at some of the things that Rails provides out of the box that are still applicable to API applications.
35
+
36
+ #### Handled at the middleware layer:
37
+
38
+ * Reloading: Rails applications support transparent reloading. This works even if your application gets big and restarting the server for every request becomes non-viable.
39
+ * Development Mode: Rails application come with smart defaults for development, making development pleasant without compromising production-time performance.
40
+ * Test Mode: Ditto test mode.
41
+ * Logging: Rails applications log every request, with a level of verbosity appropriate for the current mode. Rails logs in development include information about the request environment, database queries, and basic performance information.
42
+ * Security: Rails detects and thwarts [IP spoofing attacks](http://en.wikipedia.org/wiki/IP_address_spoofing) and handles cryptographic signatures in a [timing attack](http://en.wikipedia.org/wiki/Timing_attack) aware way. Don't know what an IP spoofing attack or a timing attack is? Exactly.
43
+ * Parameter Parsing: Want to specify your parameters as JSON instead of as a URL-encoded String? No problem. Rails will decode the JSON for you and make it available in *params*. Want to use nested URL-encoded params? That works too.
44
+ * Conditional GETs: Rails handles conditional *GET*, (*ETag* and *Last-Modified*), processing request headers and returning the correct response headers and status code. All you need to do is use the [*stale?*](http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F) check in your controller, and Rails will handle all of the HTTP details for you.
45
+ * Caching: If you use *dirty?* with public cache control, Rails will automatically cache your responses. You can easily configure the cache store.
46
+ * HEAD requests: Rails will transparently convert *HEAD* requests into *GET* requests, and return just the headers on the way out. This makes *HEAD* work reliably in all Rails APIs.
47
+
48
+ While you could obviously build these up in terms of existing Rack middlewares, I think this list demonstrates that the default Rails middleware stack provides a lot of value, even if you're "just generating JSON".
49
+
50
+ #### Handled at the ActionPack layer:
51
+
52
+ * Resourceful Routing: If you're building a RESTful JSON API, you want to be using the Rails router. Clean and conventional mapping from HTTP to controllers means not having to spend time thinking about how to model your API in terms of HTTP.
53
+ * URL Generation: The flip side of routing is URL generation. A good API based on HTTP includes URLs (see [the GitHub gist API](http://developer.github.com/v3/gists/) for an example).
54
+ * Header and Redirection Responses: `head :no_content` and `redirect_to user_url(current_user)` come in handy. Sure, you could manually add the response headers, but why?
55
+ * Caching: Rails provides page, action and fragment caching. Fragment caching is especially helpful when building up a nested JSON object.
56
+ * Basic, Digest and Token Authentication: Rails comes with out-of-the-box support for three kinds of HTTP authentication.
57
+ * Instrumentation: Rails 3.0 added an instrumentation API that will trigger registered handlers for a variety of events, such as action processing, sending a file or data, redirection, and database queries. The payload of each event comes with relevant information (for the action processing event, the payload includes the controller, action, params, request format, request method and the request's full path).
58
+ * Generators: This may be passé for advanced Rails users, but it can be nice to generate a resource and get your model, controller, test stubs, and routes created for you in a single command.
59
+ * Plugins: Many third-party libraries come with support for Rails that reduces or eliminates the cost of setting up and gluing together the library and the web framework. This includes things like overriding default generators, adding rake tasks, and honoring Rails choices (like the logger and cache backend).
60
+
61
+ Of course, the Rails boot process also glues together all registered components. For example, the Rails boot process is what uses your *config/database.yml* file when configuring ActiveRecord.
62
+
63
+ **The short version is**: you may not have thought about which parts of Rails are still applicable even if you remove the view layer, but the answer turns out to be "most of it".
64
+
65
+ ### The Basic Configuration
66
+
67
+ If you're building a Rails application that will be an API server first and foremost, you can start with a more limited subset of Rails and add in features as needed.
68
+
69
+ #### For new apps
70
+
71
+ Install the gem if you haven't already:
72
+
73
+ gem install rails-api
74
+
75
+ Then generate a new **Rails::API** app:
76
+
77
+ rails-api new my_api
78
+
79
+ This will do three main things for you:
80
+
81
+ * Make the application inherit from *Rails::ApiApplication* instead of *Rails::Application*. This will configure your application to start with a more limited set of middleware than normal. Specifically, it will not include any middleware primarily useful for browser applications (like cookie support) by default.
82
+ * Make *ApplicationController* inherit from *ActionController::API* instead of *ActionController::Base*. As with middleware, this will leave out any *ActionController* modules that provide functionality primarily used by browser applications.
83
+ * Configure the generators to skip generating views, helpers and assets when you generate a new resource.
84
+
85
+ #### For already existing apps
86
+
87
+ If you want to take an existing app and make it a **Rails::API** app, you'll have to do some quick setup manually.
88
+
89
+ Add the gem to your *Gemfile*:
90
+
91
+ gem 'rails-api'
92
+
93
+ And run `bundle` to install the gem.
94
+
95
+ In *config/application.rb*, change your *Application* class to inherit from *Rails::ApiApplication*:
96
+
97
+ ```ruby
98
+ # instead of
99
+ module MyApi
100
+ class Application < Rails::Application
101
+ end
102
+ end
103
+
104
+ # do
105
+ module MyApi
106
+ class Application < Rails::ApiApplication
107
+ end
108
+ end
109
+ ```
110
+
111
+ Change *app/controllers/application_controller.rb*:
112
+
113
+ ```ruby
114
+ # instead of
115
+ class ApplicationController < ActionController::Base
116
+ end
117
+
118
+ # do
119
+ class ApplicationController < ActionController::API
120
+ end
121
+ ```
122
+
123
+ And comment out the `protect_from_forgery` call if you are using it.
124
+
125
+ ### Choosing Middlewares
126
+
127
+ An api application comes with the following middlewares by default.
128
+
129
+ * *Rack::Cache*: Caches responses with public *Cache-Control* headers using HTTP caching semantics.
130
+ * *Rack::Sendfile*: Uses a front-end server's file serving support from your Rails application.
131
+ * *Rack::Lock*: If your application is not marked as threadsafe (`config.threadsafe!`), this middleware will add a mutex around your requests.
132
+ * *ActionDispatch::RequestId*
133
+ * *Rails::Rack::Logger*
134
+ * *Rack::Runtime*: Adds a header to the response listing the total runtime of the request.
135
+ * *ActionDispatch::ShowExceptions*: Rescue exceptions and re-dispatch them to an exception handling application.
136
+ * *ActionDispatch::DebugExceptions*: Log exceptions.
137
+ * *ActionDispatch::RemoteIp*: Protect against IP spoofing attacks.
138
+ * *ActionDispatch::Reloader*: In development mode, support code reloading.
139
+ * *ActionDispatch::ParamsParser*: Parse XML, YAML and JSON parameters when the request's *Content-Type* is one of those.
140
+ * *ActionDispatch::Head*: Dispatch *HEAD* requests as *GET* requests, and return only the status code and headers.
141
+ * *Rack::ConditionalGet*: Supports the `stale?` feature in Rails controllers.
142
+ * *Rack::ETag*: Automatically set an *ETag* on all string responses. This means that if the same response is returned from a controller for the same URL, the server will return a *304 Not Modified*, even if no additional caching steps are taken. This is primarily a client-side optimization; it reduces bandwidth costs but not server processing time.
143
+
144
+ Other plugins, including *ActiveRecord*, may add additional middlewares. In general, these middlewares are agnostic to the type of app you are building, and make sense in an API-only Rails application.
145
+
146
+ You can get a list of all middlewares in your application via:
147
+
148
+ rake middleware
149
+
150
+ #### Other Middlewares
151
+
152
+ Rails ships with a number of other middlewares that you might want to use in an API app, especially if one of your API clients is the browser:
153
+
154
+ * *Rack::MethodOverride*: Allows the use of the *_method* hack to route POST requests to other verbs.
155
+ * *ActionDispatch::Cookies*: Supports the *cookie* method in *ActionController*, including support for signed and encrypted cookies.
156
+ * *ActionDispatch::Flash*: Supports the *flash* mechanism in *ActionController*.
157
+ * *ActionDispatch::BestStandards*: Tells Internet Explorer to use the most standards-compliant available renderer. In production mode, if ChromeFrame is available, use ChromeFrame.
158
+ * Session Management: If a *config.session_store* is supplied, this middleware makes the session available as the *session* method in *ActionController*.
159
+
160
+ Any of these middlewares can be added via:
161
+
162
+ ```ruby
163
+ config.middleware.use Rack::MethodOverride
164
+ ```
165
+
166
+ #### Removing Middlewares
167
+
168
+ If you don't want to use a middleware that is included by default in the api middleware set, you can remove it using *config.middleware.delete*:
169
+
170
+ ```ruby
171
+ config.middleware.delete ::Rack::Sendfile
172
+ ```
173
+
174
+ Keep in mind that removing these features may remove support for certain features in *ActionController*.
175
+
176
+ ### Choosing Controller Modules
177
+
178
+ An api application (using *ActionController::API*) comes with the following controller modules by default:
179
+
180
+ * *ActionController::UrlFor*: Makes *url_for* and friends available
181
+ * *ActionController::Redirecting*: Support for *redirect_to*
182
+ * *ActionController::Rendering*: Basic support for rendering
183
+ * *ActionController::Renderers::All*: Support for *render :json* and friends
184
+ * *ActionController::ConditionalGet*: Support for *stale?*
185
+ * *ActionController::ForceSSL*: Support for *force_ssl*
186
+ * *ActionController::RackDelegation*: Support for the *request* and *response* methods returning *ActionDispatch::Request* and *ActionDispatch::Response* objects.
187
+ * *ActionController::DataStreaming*: Support for *send_file* and *send_data*
188
+ * *AbstractController::Callbacks*: Support for *before_filter* and friends
189
+ * *ActionController::Instrumentation*: Support for the instrumentation hooks defined by *ActionController* (see [the source](https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/instrumentation.rb) for more).
190
+ * *ActionController::Rescue*: Support for *rescue_from*.
191
+
192
+ Other plugins may add additional modules. You can get a list of all modules included into *ActionController::API* in the rails console:
193
+
194
+ ```ruby
195
+ ActionController::API.ancestors - ActionController::Metal.ancestors
196
+ ```
197
+
198
+ #### Adding Other Modules
199
+
200
+ All Action Controller modules know about their dependent modules, so you can feel free to include any modules into your controllers, and all dependencies will be included and set up as well.
201
+
202
+ Some common modules you might want to add:
203
+
204
+ * *AbstractController::Translation*: Support for the *l* and *t* localization and translation methods. These delegate to *I18n.translate* and *I18n.localize*.
205
+ * *ActionController::HTTPAuthentication::Basic* (or *Digest* or *Token*): Support for basic, digest or token HTTP authentication.
206
+ * *AbstractController::Layouts*: Support for layouts when rendering.
207
+ * *ActionController::MimeResponds*: Support for content negotiation (*respond_to*, *respond_with*).
208
+ * *ActionController::Cookies*: Support for *cookies*, which includes support for signed and encrypted cookies. This requires the cookie middleware.
209
+
210
+ The best place to add a module is in your *ApplicationController*. You can also add modules to individual controllers.
211
+
212
+
213
+ ## Contributing
214
+
215
+ 1. Fork it
216
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
217
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
218
+ 4. Push to the branch (`git push origin my-new-feature`)
219
+ 5. Create new Pull Request
220
+
221
+ ## Maintainers
222
+
223
+ * Santiago Pastorino (https://github.com/spastorino)
224
+ * Carlos Antonio da Silva (https://github.com/carlosantoniodasilva)
225
+
226
+ ## License
227
+
228
+ MIT License.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rails-api/generators/rails/app/app_generator'
4
+ require 'rails/cli'
@@ -0,0 +1,16 @@
1
+ require 'rails/generators/rails/resource_route/resource_route_generator'
2
+
3
+ module Rails
4
+ module Generators
5
+ class ApiResourceRouteGenerator < ResourceRouteGenerator
6
+ def add_resource_route
7
+ return if options[:actions].present?
8
+ route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ")
9
+ route_config << "resources :#{file_name.pluralize}"
10
+ route_config << ", except: :edit"
11
+ route_config << " end" * regular_class_path.size
12
+ route route_config
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ require 'rails-api/version'
2
+ require 'rails-api/action_controller/api'
3
+ require 'rails-api/application'
@@ -0,0 +1,153 @@
1
+ require 'action_controller'
2
+ require 'action_controller/log_subscriber'
3
+
4
+ module ActionController
5
+ # API Controller is a lightweight version of <tt>ActionController::Base</tt>,
6
+ # created for applications that don't require all functionality that a complete
7
+ # \Rails controller provides, allowing you to create faster controllers for
8
+ # example for API only applications.
9
+ #
10
+ # An API Controller is different from a normal controller in the sense that
11
+ # by default it doesn't include a number of features that are usually required
12
+ # by browser access only: layouts and templates rendering, cookies, sessions,
13
+ # flash, assets, and so on. This makes the entire controller stack thinner and
14
+ # faster, suitable for API applications. It doesn't mean you won't have such
15
+ # features if you need them: they're all available for you to include in
16
+ # your application, they're just not part of the default API Controller stack.
17
+ #
18
+ # By default, only the ApplicationController in a \Rails application inherits
19
+ # from <tt>ActionController::API</tt>. All other controllers in turn inherit
20
+ # from ApplicationController.
21
+ #
22
+ # A sample controller could look like this:
23
+ #
24
+ # class PostsController < ApplicationController
25
+ # def index
26
+ # @posts = Post.all
27
+ # render json: @posts
28
+ # end
29
+ # end
30
+ #
31
+ # Request, response and parameters objects all work the exact same way as
32
+ # <tt>ActionController::Base</tt>.
33
+ #
34
+ # == Renders
35
+ #
36
+ # The default API Controller stack includes all renderers, which means you
37
+ # can use <tt>render :json</tt> and brothers freely in your controllers. Keep
38
+ # in mind that templates are not going to be rendered, so you need to ensure
39
+ # your controller is calling either <tt>render</tt> or <tt>redirect</tt> in
40
+ # all actions.
41
+ #
42
+ # def show
43
+ # @post = Post.find(params[:id])
44
+ # render json: @post
45
+ # end
46
+ #
47
+ # == Redirects
48
+ #
49
+ # Redirects are used to move from one action to another. You can use the
50
+ # <tt>redirect</tt> method in your controllers in the same way as
51
+ # <tt>ActionController::Base</tt>. For example:
52
+ #
53
+ # def create
54
+ # redirect_to root_url and return if not_authorized?
55
+ # # do stuff here
56
+ # end
57
+ #
58
+ # == Adding new behavior
59
+ #
60
+ # In some scenarios you may want to add back some functionality provided by
61
+ # <tt>ActionController::Base</tt> that is not present by default in
62
+ # <tt>ActionController::API</tt>, for instance <tt>MimeResponds</tt>. This
63
+ # module gives you the <tt>respond_to</tt> and <tt>respond_with</tt> methods.
64
+ # Adding it is quite simple, you just need to include the module in a specific
65
+ # controller or in <tt>ApplicationController</tt> in case you want it
66
+ # available to your entire app:
67
+ #
68
+ # class ApplicationController < ActionController::API
69
+ # include ActionController::MimeResponds
70
+ # end
71
+ #
72
+ # class PostsController < ApplicationController
73
+ # respond_to :json, :xml
74
+ #
75
+ # def index
76
+ # @posts = Post.all
77
+ # respond_with @posts
78
+ # end
79
+ # end
80
+ #
81
+ # Quite straightforward. Make sure to check <tt>ActionController::Base</tt>
82
+ # available modules if you want to include any other functionality that is
83
+ # not provided by <tt>ActionController::API</tt> out of the box.
84
+ class API < Metal
85
+ abstract!
86
+
87
+ module Compabitility
88
+ def cache_store; end
89
+ def cache_store=(*); end
90
+ def assets_dir=(*); end
91
+ def javascripts_dir=(*); end
92
+ def stylesheets_dir=(*); end
93
+ def page_cache_directory=(*); end
94
+ def asset_path=(*); end
95
+ def asset_host=(*); end
96
+ def relative_url_root=(*); end
97
+ def perform_caching=(*); end
98
+ def wrap_parameters(*); end
99
+ def helpers_path=(*); end
100
+ def allow_forgery_protection=(*); end
101
+ end
102
+
103
+ extend Compabitility
104
+
105
+ # Shortcut helper that returns all the ActionController::API modules except the ones passed in the argument:
106
+ #
107
+ # class MetalController
108
+ # ActionController::API.without_modules(:Redirecting, :DataStreaming).each do |left|
109
+ # include left
110
+ # end
111
+ # end
112
+ #
113
+ # This gives better control over what you want to exclude and makes it easier
114
+ # to create an api controller class, instead of listing the modules required manually.
115
+ def self.without_modules(*modules)
116
+ modules = modules.map do |m|
117
+ m.is_a?(Symbol) ? ActionController.const_get(m) : m
118
+ end
119
+
120
+ MODULES - modules
121
+ end
122
+
123
+ MODULES = [
124
+ HideActions,
125
+ UrlFor,
126
+ Redirecting,
127
+ Rendering,
128
+ Renderers::All,
129
+ ConditionalGet,
130
+ RackDelegation,
131
+
132
+ ForceSSL,
133
+ DataStreaming,
134
+
135
+ # Before callbacks should also be executed the earliest as possible, so
136
+ # also include them at the bottom.
137
+ AbstractController::Callbacks,
138
+
139
+ # Append rescue at the bottom to wrap as much as possible.
140
+ Rescue,
141
+
142
+ # Add instrumentations hooks at the bottom, to ensure they instrument
143
+ # all the methods properly.
144
+ Instrumentation
145
+ ]
146
+
147
+ MODULES.each do |mod|
148
+ include mod
149
+ end
150
+
151
+ ActiveSupport.run_load_hooks(:action_controller, self)
152
+ end
153
+ end
@@ -0,0 +1,94 @@
1
+ require 'rails/version'
2
+ require 'rails/application'
3
+
4
+ module Rails
5
+ class Application < Engine
6
+ def default_middleware_stack
7
+ ActionDispatch::MiddlewareStack.new.tap do |middleware|
8
+ if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
9
+ require "action_dispatch/http/rack_cache"
10
+ middleware.use ::Rack::Cache, rack_cache
11
+ end
12
+
13
+ if config.force_ssl
14
+ middleware.use ::ActionDispatch::SSL, config.ssl_options
15
+ end
16
+
17
+ if config.action_dispatch.x_sendfile_header.present?
18
+ middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
19
+ end
20
+
21
+ if config.serve_static_assets
22
+ middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
23
+ end
24
+
25
+ middleware.use ::Rack::Lock unless config.allow_concurrency
26
+ middleware.use ::Rack::Runtime
27
+ middleware.use ::ActionDispatch::RequestId
28
+ middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods
29
+ middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
30
+ middleware.use ::ActionDispatch::DebugExceptions
31
+ middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
32
+
33
+ unless config.cache_classes
34
+ app = self
35
+ middleware.use ::ActionDispatch::Reloader, lambda { app.reload_dependencies? }
36
+ end
37
+
38
+ middleware.use ::ActionDispatch::Callbacks
39
+
40
+ middleware.use ::ActionDispatch::ParamsParser
41
+ middleware.use ::ActionDispatch::Head
42
+ middleware.use ::Rack::ConditionalGet
43
+ middleware.use ::Rack::ETag, "no-cache"
44
+ end
45
+ end
46
+
47
+ if Rails::VERSION::STRING <= "3.2.3"
48
+ def load_generators(app=self)
49
+ super
50
+ require 'rails/generators/rails/resource/resource_generator'
51
+ Rails::Generators::ResourceGenerator.class_eval do
52
+ def add_resource_route
53
+ return if options[:actions].present?
54
+ route_config = regular_class_path.collect{|namespace| "namespace :#{namespace} do " }.join(" ")
55
+ route_config << "resources :#{file_name.pluralize}"
56
+ route_config << ", except: :edit"
57
+ route_config << " end" * regular_class_path.size
58
+ route route_config
59
+ end
60
+ end
61
+ self
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def setup_generators!
68
+ generators = config.generators
69
+
70
+ generators.templates.unshift File::expand_path('../templates', __FILE__)
71
+ if Rails::VERSION::STRING > "3.2.3"
72
+ generators.resource_route = :api_resource_route
73
+ end
74
+
75
+ %w(assets css js session_migration).each do |namespace|
76
+ generators.hide_namespace namespace
77
+ end
78
+
79
+ generators.rails({
80
+ :assets => false,
81
+ :helper => false,
82
+ :javascripts => false,
83
+ :javascript_engine => nil,
84
+ :stylesheets => false,
85
+ :stylesheet_engine => nil,
86
+ :template_engine => nil
87
+ })
88
+ end
89
+
90
+ ActiveSupport.on_load(:before_configuration) do
91
+ setup_generators!
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,6 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/rails/app/app_generator'
3
+
4
+ Rails::Generators::AppGenerator.source_paths.unshift(
5
+ File.expand_path('../../../../templates/rails/app', __FILE__)
6
+ )
@@ -0,0 +1,27 @@
1
+ source 'https://rubygems.org'
2
+
3
+ <%= rails_gemfile_entry -%>
4
+
5
+ gem 'rails-api'
6
+
7
+ <%= database_gemfile_entry -%>
8
+
9
+ <%= "gem 'jruby-openssl'\n" if defined?(JRUBY_VERSION) -%>
10
+ <%= "gem 'json'\n" if RUBY_VERSION < "1.9.2" -%>
11
+
12
+ <%= javascript_gemfile_entry %>
13
+
14
+ # To use ActiveModel has_secure_password
15
+ # gem 'bcrypt-ruby', '~> 3.0.0'
16
+
17
+ # To use Jbuilder templates for JSON
18
+ # gem 'jbuilder'
19
+
20
+ # Use unicorn as the app server
21
+ # gem 'unicorn'
22
+
23
+ # Deploy with Capistrano
24
+ # gem 'capistrano', :group => :development
25
+
26
+ # To use debugger
27
+ # gem 'ruby-debug19', :require => 'ruby-debug'
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::API
2
+ end
@@ -0,0 +1,60 @@
1
+ <% module_namespacing do -%>
2
+ class <%= controller_class_name %>Controller < ApplicationController
3
+ # GET <%= route_url %>
4
+ # GET <%= route_url %>.json
5
+ def index
6
+ @<%= plural_table_name %> = <%= orm_class.all(class_name) %>
7
+
8
+ render json: <%= "@#{plural_table_name}" %>
9
+ end
10
+
11
+ # GET <%= route_url %>/1
12
+ # GET <%= route_url %>/1.json
13
+ def show
14
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
15
+
16
+ render json: <%= "@#{singular_table_name}" %>
17
+ end
18
+
19
+ # GET <%= route_url %>/new
20
+ # GET <%= route_url %>/new.json
21
+ def new
22
+ @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
23
+
24
+ render json: <%= "@#{singular_table_name}" %>
25
+ end
26
+
27
+ # POST <%= route_url %>
28
+ # POST <%= route_url %>.json
29
+ def create
30
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "params[:#{singular_table_name}]") %>
31
+
32
+ if @<%= orm_instance.save %>
33
+ render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %>
34
+ else
35
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
36
+ end
37
+ end
38
+
39
+ # PATCH/PUT <%= route_url %>/1
40
+ # PATCH/PUT <%= route_url %>/1.json
41
+ def update
42
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
43
+
44
+ if @<%= orm_instance.update_attributes("params[:#{singular_table_name}]") %>
45
+ head :no_content
46
+ else
47
+ render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity
48
+ end
49
+ end
50
+
51
+ # DELETE <%= route_url %>/1
52
+ # DELETE <%= route_url %>/1.json
53
+ def destroy
54
+ @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
55
+ @<%= orm_instance.destroy %>
56
+
57
+ head :no_content
58
+ end
59
+ end
60
+ <% end -%>
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
5
+ setup do
6
+ @<%= singular_table_name %> = <%= table_name %>(:one)
7
+ end
8
+
9
+ test "should get index" do
10
+ get :index
11
+ assert_response :success
12
+ assert_not_nil assigns(:<%= table_name %>)
13
+ end
14
+
15
+ test "should get new" do
16
+ get :new
17
+ assert_response :success
18
+ end
19
+
20
+ test "should create <%= singular_table_name %>" do
21
+ assert_difference('<%= class_name %>.count') do
22
+ post :create, <%= "#{singular_table_name}: { #{attributes_hash} }" %>
23
+ end
24
+
25
+ assert_response 201
26
+ end
27
+
28
+ test "should show <%= singular_table_name %>" do
29
+ get :show, id: <%= "@#{singular_table_name}" %>
30
+ assert_response :success
31
+ end
32
+
33
+ test "should update <%= singular_table_name %>" do
34
+ put :update, id: <%= "@#{singular_table_name}" %>, <%= "#{singular_table_name}: { #{attributes_hash} }" %>
35
+ assert_response 204
36
+ end
37
+
38
+ test "should destroy <%= singular_table_name %>" do
39
+ assert_difference('<%= class_name %>.count', -1) do
40
+ delete :destroy, id: <%= "@#{singular_table_name}" %>
41
+ end
42
+
43
+ assert_response 204
44
+ end
45
+ end
46
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ module Rails
2
+ module API
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+ require 'action_controller/railtie'
3
+ require 'rack/test'
4
+
5
+ class OmgController < ActionController::API
6
+ def index
7
+ render :text => "OMG"
8
+ end
9
+ end
10
+
11
+ class ApiApplicationTest < ActiveSupport::TestCase
12
+ include ::Rack::Test::Methods
13
+
14
+ def test_boot_api_app
15
+ app.initialize!
16
+
17
+ get "/omg"
18
+ assert_equal 200, last_response.status
19
+ assert_equal "OMG", last_response.body
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class ActionMethodsApiController < ActionController::API
4
+ def one; end
5
+ def two; end
6
+ hide_action :two
7
+ end
8
+
9
+ class ActionMethodsApiTest < ActionController::TestCase
10
+ tests ActionMethodsApiController
11
+
12
+ def test_action_methods
13
+ assert_equal Set.new(%w(one)),
14
+ @controller.class.action_methods,
15
+ "#{@controller.controller_path} should not be empty!"
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+ require 'active_support/core_ext/integer/time'
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ class ConditionalGetApiController < ActionController::API
6
+ before_filter :handle_last_modified_and_etags, :only => :two
7
+
8
+ def one
9
+ if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123])
10
+ render :text => "Hi!"
11
+ end
12
+ end
13
+
14
+ def two
15
+ render :text => "Hi!"
16
+ end
17
+
18
+ private
19
+
20
+ def handle_last_modified_and_etags
21
+ fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ])
22
+ end
23
+ end
24
+
25
+ class ConditionalGetApiTest < ActionController::TestCase
26
+ tests ConditionalGetApiController
27
+
28
+ def setup
29
+ @last_modified = Time.now.utc.beginning_of_day.httpdate
30
+ end
31
+
32
+ def test_request_with_bang_gets_last_modified
33
+ get :two
34
+ assert_equal @last_modified, @response.headers['Last-Modified']
35
+ assert_response :success
36
+ end
37
+
38
+ def test_request_with_bang_obeys_last_modified
39
+ @request.if_modified_since = @last_modified
40
+ get :two
41
+ assert_response :not_modified
42
+ end
43
+
44
+ def test_last_modified_works_with_less_than_too
45
+ @request.if_modified_since = 5.years.ago.httpdate
46
+ get :two
47
+ assert_response :success
48
+ end
49
+
50
+ def test_request_not_modified
51
+ @request.if_modified_since = @last_modified
52
+ get :one
53
+ assert_equal 304, @response.status.to_i
54
+ assert_blank @response.body
55
+ assert_equal @last_modified, @response.headers['Last-Modified']
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ module TestApiFileUtils
4
+ def file_name() File.basename(__FILE__) end
5
+ def file_path() File.expand_path(__FILE__) end
6
+ def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end
7
+ end
8
+
9
+ class DataStreamingApiController < ActionController::API
10
+ include TestApiFileUtils
11
+
12
+ def one; end
13
+ def two
14
+ send_data(file_data, {})
15
+ end
16
+ end
17
+
18
+ class DataStreamingApiTest < ActionController::TestCase
19
+ include TestApiFileUtils
20
+ tests DataStreamingApiController
21
+
22
+ def test_data
23
+ response = process('two')
24
+ assert_kind_of String, response.body
25
+ assert_equal file_data, response.body
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class ForceSSLApiController < ActionController::API
4
+ force_ssl
5
+
6
+ def one; end
7
+ def two
8
+ head :ok
9
+ end
10
+ end
11
+
12
+ class ForceSSLApiTest < ActionController::TestCase
13
+ tests ForceSSLApiController
14
+
15
+ def test_redirects_to_https
16
+ get :two
17
+ assert_response 301
18
+ assert_equal "https://test.host/force_ssl_api/two", redirect_to_url
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ class RedirectToApiController < ActionController::API
4
+ def one
5
+ redirect_to :action => "two"
6
+ end
7
+
8
+ def two; end
9
+ end
10
+
11
+ class RedirectToApiTest < ActionController::TestCase
12
+ tests RedirectToApiController
13
+
14
+ def test_redirect_to
15
+ get :one
16
+ assert_response :redirect
17
+ assert_equal "http://test.host/redirect_to_api/two", redirect_to_url
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ require 'test_helper'
2
+ require 'active_support/core_ext/hash/conversions'
3
+
4
+ class Model
5
+ def to_json(options = {})
6
+ { :a => 'b' }.to_json(options)
7
+ end
8
+
9
+ def to_xml(options = {})
10
+ { :a => 'b' }.to_xml(options)
11
+ end
12
+ end
13
+
14
+ class RenderersApiController < ActionController::API
15
+ def one
16
+ render :json => Model.new
17
+ end
18
+
19
+ def two
20
+ render :xml => Model.new
21
+ end
22
+ end
23
+
24
+ class RenderersApiTest < ActionController::TestCase
25
+ tests RenderersApiController
26
+
27
+ def test_render_json
28
+ get :one
29
+ assert_response :success
30
+ assert_equal({ :a => 'b' }.to_json, @response.body)
31
+ end
32
+
33
+ def test_render_xml
34
+ get :two
35
+ assert_response :success
36
+ assert_equal({ :a => 'b' }.to_xml, @response.body)
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+
3
+ class UrlForApiController < ActionController::API
4
+ def one; end
5
+ def two; end
6
+ end
7
+
8
+ class UrlForApiTest < ActionController::TestCase
9
+ tests UrlForApiController
10
+
11
+ def setup
12
+ super
13
+ @request.host = 'www.example.com'
14
+ end
15
+
16
+ def test_url_for
17
+ get :one
18
+ assert_equal "http://www.example.com/url_for_api/one", @controller.url_for
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ require 'generators/generators_test_helper'
2
+ require 'rails-api/generators/rails/app/app_generator'
3
+
4
+ class AppGeneratorTest < Rails::Generators::TestCase
5
+ tests Rails::Generators::AppGenerator
6
+
7
+ arguments [destination_root]
8
+
9
+ def test_skeleton_is_created
10
+ run_generator
11
+
12
+ default_files.each { |path| assert_file path }
13
+ end
14
+
15
+ def test_api_modified_files
16
+ run_generator
17
+
18
+ assert_file "Gemfile" do |content|
19
+ assert_match(/gem 'rails-api'/, content)
20
+ assert_no_match(/gem 'coffee-rails'/, content)
21
+ assert_no_match(/gem 'sass-rails'/, content)
22
+ end
23
+ assert_file "app/controllers/application_controller.rb", /ActionController::API/
24
+ end
25
+
26
+ private
27
+
28
+ def default_files
29
+ %w(.gitignore
30
+ Gemfile
31
+ Rakefile
32
+ config.ru
33
+ app/controllers
34
+ app/mailers
35
+ app/models
36
+ config/environments
37
+ config/initializers
38
+ config/locales
39
+ db
40
+ doc
41
+ lib
42
+ lib/tasks
43
+ lib/assets
44
+ log
45
+ script/rails
46
+ test/fixtures
47
+ test/functional
48
+ test/integration
49
+ test/performance
50
+ test/unit
51
+ vendor
52
+ vendor/assets
53
+ tmp/cache
54
+ tmp/cache/assets)
55
+ end
56
+ end
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,23 @@
1
+ require 'test_helper'
2
+ require 'rails/generators/test_case'
3
+
4
+ class Rails::Generators::TestCase
5
+ destination File.expand_path("../../tmp", __FILE__)
6
+
7
+ def setup
8
+ mkdir_p destination_root
9
+ end
10
+
11
+ def teardown
12
+ rm_rf destination_root
13
+ end
14
+
15
+ private
16
+
17
+ def copy_routes
18
+ routes = File.expand_path("../fixtures/routes.rb", __FILE__)
19
+ destination = File.join(destination_root, "config")
20
+ FileUtils.mkdir_p(destination)
21
+ FileUtils.cp routes, destination
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ require 'generators/generators_test_helper'
2
+ require 'rails/generators/rails/resource/resource_generator'
3
+
4
+ class ResourceGeneratorTest < Rails::Generators::TestCase
5
+ tests Rails::Generators::ResourceGenerator
6
+
7
+ arguments %w(account)
8
+
9
+ def setup
10
+ super
11
+ copy_routes
12
+ end
13
+
14
+ def test_resource_routes_are_added
15
+ run_generator
16
+
17
+ assert_file "config/routes.rb" do |route|
18
+ assert_match(/resources :accounts, except: :edit$/, route)
19
+ assert_no_match(/resources :accounts$/, route)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,90 @@
1
+ require 'generators/generators_test_helper'
2
+ require 'rails/generators/rails/scaffold/scaffold_generator'
3
+
4
+ class ScaffoldGeneratorTest < Rails::Generators::TestCase
5
+ tests Rails::Generators::ScaffoldGenerator
6
+
7
+ arguments %w(product_line title:string product:belongs_to user:references)
8
+
9
+ def setup
10
+ super
11
+ copy_routes
12
+ end
13
+
14
+ def test_scaffold_on_invoke
15
+ run_generator
16
+
17
+ # Model
18
+ assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
19
+ assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
20
+ assert_file "test/fixtures/product_lines.yml"
21
+ assert_migration "db/migrate/create_product_lines.rb",
22
+ /belongs_to :product/,
23
+ /add_index :product_lines, :product_id/,
24
+ /references :user/,
25
+ /add_index :product_lines, :user_id/
26
+
27
+ # Route
28
+ assert_file "config/routes.rb" do |content|
29
+ assert_match(/resources :product_lines, except: :edit$/, content)
30
+ assert_no_match(/resource :product_lines$/, content)
31
+ end
32
+
33
+ # Controller
34
+ assert_file "app/controllers/product_lines_controller.rb" do |content|
35
+ assert_match(/class ProductLinesController < ApplicationController/, content)
36
+ assert_no_match(/respond_to/, content)
37
+
38
+ assert_instance_method :index, content do |m|
39
+ assert_match(/@product_lines = ProductLine\.all/, m)
40
+ end
41
+
42
+ assert_instance_method :show, content do |m|
43
+ assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
44
+ end
45
+
46
+ assert_instance_method :new, content do |m|
47
+ assert_match(/@product_line = ProductLine\.new/, m)
48
+ end
49
+
50
+ assert_instance_method :create, content do |m|
51
+ assert_match(/@product_line = ProductLine\.new\(params\[:product_line\]\)/, m)
52
+ assert_match(/@product_line\.save/, m)
53
+ assert_match(/@product_line\.errors/, m)
54
+ end
55
+
56
+ assert_instance_method :update, content do |m|
57
+ assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
58
+ assert_match(/@product_line\.update_attributes\(params\[:product_line\]\)/, m)
59
+ assert_match(/@product_line\.errors/, m)
60
+ end
61
+
62
+ assert_instance_method :destroy, content do |m|
63
+ assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m)
64
+ assert_match(/@product_line\.destroy/, m)
65
+ end
66
+ end
67
+
68
+ assert_file "test/functional/product_lines_controller_test.rb" do |test|
69
+ assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
70
+ assert_match(/post :create, product_line: \{ title: @product_line.title \}/, test)
71
+ assert_match(/put :update, id: @product_line, product_line: \{ title: @product_line.title \}/, test)
72
+ assert_no_match(/assert_redirected_to/, test)
73
+ end
74
+
75
+ # Views
76
+ %w(index edit new show _form).each do |view|
77
+ assert_no_file "app/views/product_lines/#{view}.html.erb"
78
+ end
79
+ assert_no_file "app/views/layouts/product_lines.html.erb"
80
+
81
+ # Helpers
82
+ assert_no_file "app/helpers/product_lines_helper.rb"
83
+ assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
84
+
85
+ # Assets
86
+ assert_no_file "app/assets/stylesheets/scaffold.css"
87
+ assert_no_file "app/assets/javascripts/product_lines.js"
88
+ assert_no_file "app/assets/stylesheets/product_lines.css"
89
+ end
90
+ end
@@ -0,0 +1,41 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require 'rails'
5
+ require 'rails/test_help'
6
+ require 'rails-api'
7
+
8
+ def app
9
+ @@app ||= Class.new(Rails::Application) do
10
+ config.active_support.deprecation = :stderr
11
+ config.generators do |c|
12
+ c.orm :active_record, :migration => true,
13
+ :timestamps => true
14
+
15
+ c.test_framework :test_unit, :fixture => true,
16
+ :fixture_replacement => nil
17
+
18
+ c.integration_tool :test_unit
19
+ c.performance_tool :test_unit
20
+ end
21
+
22
+ def self.name
23
+ 'TestApp'
24
+ end
25
+ end
26
+ end
27
+
28
+ app.routes.append do
29
+ match ':controller(/:action)'
30
+ end
31
+ app.routes.finalize!
32
+
33
+ module ActionController
34
+ class API
35
+ include app.routes.url_helpers
36
+ end
37
+ end
38
+
39
+ app.load_generators
40
+
41
+ Rails.logger = Logger.new("/dev/null")
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-api
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Santiago Pastorino and Carlos Antonio da Silva
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-20 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: actionpack
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 3
32
+ - 2
33
+ - 0
34
+ version: 3.2.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: railties
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 15
46
+ segments:
47
+ - 3
48
+ - 2
49
+ - 0
50
+ version: 3.2.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: tzinfo
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 45
62
+ segments:
63
+ - 0
64
+ - 3
65
+ - 31
66
+ version: 0.3.31
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 15
78
+ segments:
79
+ - 3
80
+ - 2
81
+ - 0
82
+ version: 3.2.0
83
+ type: :development
84
+ version_requirements: *id004
85
+ description: |-
86
+ Rails::API is a subset of a normal Rails application,
87
+ created for applications that don't require all
88
+ functionality that a complete Rails application provides
89
+ email:
90
+ - <santiago@wyeworks.com>
91
+ - <carlosantoniodasilva@gmail.com>
92
+ executables:
93
+ - rails-api
94
+ extensions: []
95
+
96
+ extra_rdoc_files: []
97
+
98
+ files:
99
+ - README.md
100
+ - bin/rails-api
101
+ - lib/generators/rails/api_resource_route/api_resource_route_generator.rb
102
+ - lib/rails-api/action_controller/api.rb
103
+ - lib/rails-api/application.rb
104
+ - lib/rails-api/generators/rails/app/app_generator.rb
105
+ - lib/rails-api/templates/rails/app/app/controllers/application_controller.rb.tt
106
+ - lib/rails-api/templates/rails/app/Gemfile
107
+ - lib/rails-api/templates/rails/scaffold_controller/controller.rb
108
+ - lib/rails-api/templates/test_unit/scaffold/functional_test.rb
109
+ - lib/rails-api/version.rb
110
+ - lib/rails-api.rb
111
+ - test/api_application/api_application_test.rb
112
+ - test/api_controller/action_methods_test.rb
113
+ - test/api_controller/conditional_get_test.rb
114
+ - test/api_controller/data_streaming_test.rb
115
+ - test/api_controller/force_ssl_test.rb
116
+ - test/api_controller/redirect_to_test.rb
117
+ - test/api_controller/renderers_test.rb
118
+ - test/api_controller/url_for_test.rb
119
+ - test/generators/app_generator_test.rb
120
+ - test/generators/fixtures/routes.rb
121
+ - test/generators/generators_test_helper.rb
122
+ - test/generators/resource_generator_test.rb
123
+ - test/generators/scaffold_generator_test.rb
124
+ - test/test_helper.rb
125
+ has_rdoc: true
126
+ homepage: https://github.com/spastorino/rails-api
127
+ licenses:
128
+ - MIT
129
+ post_install_message:
130
+ rdoc_options: []
131
+
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ hash: 3
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 23
149
+ segments:
150
+ - 1
151
+ - 3
152
+ - 6
153
+ version: 1.3.6
154
+ requirements: []
155
+
156
+ rubyforge_project:
157
+ rubygems_version: 1.3.7
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: Rails for API only Applications
161
+ test_files:
162
+ - test/api_application/api_application_test.rb
163
+ - test/api_controller/action_methods_test.rb
164
+ - test/api_controller/conditional_get_test.rb
165
+ - test/api_controller/data_streaming_test.rb
166
+ - test/api_controller/force_ssl_test.rb
167
+ - test/api_controller/redirect_to_test.rb
168
+ - test/api_controller/renderers_test.rb
169
+ - test/api_controller/url_for_test.rb
170
+ - test/generators/app_generator_test.rb
171
+ - test/generators/fixtures/routes.rb
172
+ - test/generators/generators_test_helper.rb
173
+ - test/generators/resource_generator_test.rb
174
+ - test/generators/scaffold_generator_test.rb
175
+ - test/test_helper.rb