Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AutoLoad] Autoload Fixes #1904

Merged
merged 9 commits into from
Sep 6, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Features

* Your contribution here.
* [#1904](https://github.com/ruby-grape/grape/pull/1904): Allows Grape to load files on startup rather than on the first call - [@myxoh](https://github.com/myxoh).

#### Fixes

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Mounting](#mounting)
- [All](#all)
- [Rack](#rack)
- [ActiveRecord without Rails](#activerecord-without-rails)
- [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
Expand Down Expand Up @@ -261,6 +262,17 @@ end

## Mounting

### All


By default Grape will compile the routes on the first route, it is possible to pre-load routes using the `compile!` method.

```ruby
Twitter::API.compile!
```

This can be added to your `config.ru` (if using rackup), `application.rb` (if using rails), or any file that loads your server.

### Rack

The above sample creates a Rack application that can be run from a rackup `config.ru` file
Expand All @@ -270,6 +282,13 @@ with `rackup`:
run Twitter::API
```

(With pre-loading you can use)

```ruby
Twitter::API.compile!
run Twitter::API
```

And would respond to the following routes:

GET /api/statuses/public_timeline
Expand Down
156 changes: 90 additions & 66 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,106 +54,124 @@ module Http

module Exceptions
extend ::ActiveSupport::Autoload
autoload :Base
autoload :Validation
autoload :ValidationArrayErrors
autoload :ValidationErrors
autoload :MissingVendorOption
autoload :MissingMimeType
autoload :MissingOption
autoload :InvalidFormatter
autoload :InvalidVersionerOption
autoload :UnknownValidator
autoload :UnknownOptions
autoload :UnknownParameter
autoload :InvalidWithOptionForRepresent
autoload :IncompatibleOptionValues
autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
autoload :InvalidMessageBody
autoload :InvalidAcceptHeader
autoload :InvalidVersionHeader
autoload :MethodNotAllowed
autoload :InvalidResponse
eager_autoload do
autoload :Base
autoload :Validation
autoload :ValidationArrayErrors
autoload :ValidationErrors
autoload :MissingVendorOption
autoload :MissingMimeType
autoload :MissingOption
autoload :InvalidFormatter
autoload :InvalidVersionerOption
autoload :UnknownValidator
autoload :UnknownOptions
autoload :UnknownParameter
autoload :InvalidWithOptionForRepresent
autoload :IncompatibleOptionValues
autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
autoload :InvalidMessageBody
autoload :InvalidAcceptHeader
autoload :InvalidVersionHeader
autoload :MethodNotAllowed
autoload :InvalidResponse
end
end

module Extensions
extend ::ActiveSupport::Autoload

autoload :DeepMergeableHash
autoload :DeepSymbolizeHash
autoload :DeepHashWithIndifferentAccess
autoload :Hash

eager_autoload do
autoload :DeepMergeableHash
autoload :DeepSymbolizeHash
autoload :DeepHashWithIndifferentAccess
autoload :Hash
end
module ActiveSupport
extend ::ActiveSupport::Autoload

autoload :HashWithIndifferentAccess
eager_autoload do
autoload :HashWithIndifferentAccess
end
end

module Hashie
extend ::ActiveSupport::Autoload

autoload :Mash
eager_autoload do
autoload :Mash
end
end
end

module Middleware
extend ::ActiveSupport::Autoload
autoload :Base
autoload :Versioner
autoload :Formatter
autoload :Error
autoload :Globals
autoload :Stack
eager_autoload do
autoload :Base
autoload :Versioner
autoload :Formatter
autoload :Error
autoload :Globals
autoload :Stack
end

module Auth
extend ::ActiveSupport::Autoload
autoload :Base
autoload :DSL
autoload :StrategyInfo
autoload :Strategies
eager_autoload do
autoload :Base
autoload :DSL
autoload :StrategyInfo
autoload :Strategies
end
end

module Versioner
extend ::ActiveSupport::Autoload
autoload :Path
autoload :Header
autoload :Param
autoload :AcceptVersionHeader
eager_autoload do
autoload :Path
autoload :Header
autoload :Param
autoload :AcceptVersionHeader
end
end
end

module Util
extend ::ActiveSupport::Autoload
autoload :InheritableValues
autoload :StackableValues
autoload :ReverseStackableValues
autoload :InheritableSetting
autoload :StrictHashConfiguration
autoload :Registrable
eager_autoload do
autoload :InheritableValues
autoload :StackableValues
autoload :ReverseStackableValues
autoload :InheritableSetting
autoload :StrictHashConfiguration
autoload :Registrable
end
end

module ErrorFormatter
extend ::ActiveSupport::Autoload
autoload :Base
autoload :Json
autoload :Txt
autoload :Xml
eager_autoload do
autoload :Base
autoload :Json
autoload :Txt
autoload :Xml
end
end

module Formatter
extend ::ActiveSupport::Autoload
autoload :Json
autoload :SerializableHash
autoload :Txt
autoload :Xml
eager_autoload do
autoload :Json
autoload :SerializableHash
autoload :Txt
autoload :Xml
end
end

module Parser
extend ::ActiveSupport::Autoload
autoload :Json
autoload :Xml
eager_autoload do
autoload :Json
autoload :Xml
end
end

module DSL
Expand All @@ -177,19 +195,25 @@ module DSL

class API
extend ::ActiveSupport::Autoload
autoload :Helpers
eager_autoload do
autoload :Helpers
end
end

module Presenters
extend ::ActiveSupport::Autoload
autoload :Presenter
eager_autoload do
autoload :Presenter
end
end

module ServeFile
extend ::ActiveSupport::Autoload
autoload :FileResponse
autoload :FileBody
autoload :SendfileResponse
eager_autoload do
autoload :FileResponse
autoload :FileBody
autoload :SendfileResponse
end
end
end

Expand Down
20 changes: 14 additions & 6 deletions lib/grape/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Grape
# should subclass this class in order to build an API.
class API
# Class methods that we want to call on the API rather than on the API object
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration]).freeze
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile!]).freeze

class << self
attr_accessor :base_instance, :instances
Expand Down Expand Up @@ -48,11 +48,6 @@ def override_all_methods!
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
# NOTE: This will only be called on an API directly mounted on RACK
def call(*args, &block)
instance_for_rack = if never_mounted?
base_instance
else
mounted_instances.first
end
instance_for_rack.call(*args, &block)
end

Expand Down Expand Up @@ -111,8 +106,21 @@ def method_missing(method, *args, &block)
end
end

def compile!
require 'grape/eager_load'
instance_for_rack.compile! # See API::Instance.compile!
end

private

def instance_for_rack
if never_mounted?
base_instance
else
mounted_instances.first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be all instances and the method to return an array?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. I can't remember why this should ever return one of the mounted instances, as you probably want to always be interacting with rack on a predictable instance. I'll have to see what happens if I have this always be the base_instance

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK so this change was initially introduced in: https://github.com/ruby-grape/grape/pull/1893/files
The reason why this is needed is that we used to support - before remounting was introduced - the ability to pre-mount the API you will run in a namespace (and I was maintaing backwards compatibility).

Basically you are allowed to do

class ToBeMountedinRack < Grape::API
  get 'endpoint'
end

class Namespace < Grape::API
  namespace 'namespace' do 
    mount ToBeMountedinRack
   end
end

run ToBeMountedinRack

And the expectation is that you will be able to get /namespace/endpoint
I am not sure I want to maintain this behaviour forever, as it doesn't really play nicely with re-mounting an endpoint and I don't find it very sensible (I would find it sensible to run Namespace if you want the above behavour, rather than mounting ToBeMountedinRack)

Nonetheless, I don't think this is the appropiate PR to introduce this change

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We can deprecate this separately.

end
end

# Adds a new stage to the set up require to get a Grape::API up and running
def add_setup(method, *args, &block)
setup_step = { method: method, args: args, block: block }
Expand Down
8 changes: 6 additions & 2 deletions lib/grape/api/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def change!
# the headers, and the body. See [the rack specification]
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
def call(env)
LOCK.synchronize { compile } unless instance
compile!
call!(env)
end

Expand All @@ -79,9 +79,13 @@ def cascade(value = nil)
end
end

def compile!
LOCK.synchronize { compile unless instance }
myxoh marked this conversation as resolved.
Show resolved Hide resolved
end

# see Grape::Router#recognize_path
def recognize_path(path)
LOCK.synchronize { compile } unless instance
compile!
instance.router.recognize_path(path)
end

Expand Down
18 changes: 18 additions & 0 deletions lib/grape/eager_load.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Grape.eager_load!
Grape::Http.eager_load!
Grape::Exceptions.eager_load!
Grape::Extensions.eager_load!
Grape::Extensions::ActiveSupport.eager_load!
Grape::Extensions::Hashie.eager_load!
Grape::Middleware.eager_load!
Grape::Middleware::Auth.eager_load!
Grape::Middleware::Versioner.eager_load!
Grape::Util.eager_load!
Grape::ErrorFormatter.eager_load!
Grape::Formatter.eager_load!
Grape::Parser.eager_load!
Grape::DSL.eager_load!
Grape::API.eager_load!
Grape::Presenters.eager_load!
Grape::ServeFile.eager_load!
Rack::Head # AutoLoads the Rack::Head