From f1fb6fedee39867df80e6b4140d99247e3a2cb22 Mon Sep 17 00:00:00 2001 From: Nicolas Klein Date: Tue, 3 Sep 2019 15:32:50 +0100 Subject: [PATCH 1/4] [Autoload] allows all bits of Grape to be autoloaded --- lib/grape.rb | 156 +++++++++++++++++++++++++++++---------------------- 1 file changed, 90 insertions(+), 66 deletions(-) diff --git a/lib/grape.rb b/lib/grape.rb index b48cdf42e..97f0215b6 100644 --- a/lib/grape.rb +++ b/lib/grape.rb @@ -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 @@ -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 From ae69528d4409d5b2e7dc7645218099179a51d6b7 Mon Sep 17 00:00:00 2001 From: Nicolas Klein Date: Tue, 3 Sep 2019 16:38:32 +0100 Subject: [PATCH 2/4] [AutoLoad] creates a compile! method to pre load an API class --- lib/grape/api.rb | 20 +++++++++++++------- lib/grape/api/instance.rb | 8 ++++++-- lib/grape/eager_load.rb | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 lib/grape/eager_load.rb diff --git a/lib/grape/api.rb b/lib/grape/api.rb index a830bb834..df4069a44 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -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 @@ -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 @@ -111,8 +106,19 @@ def method_missing(method, *args, &block) end end - private + 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 + 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 } diff --git a/lib/grape/api/instance.rb b/lib/grape/api/instance.rb index b53e6d6eb..4d5e2ce8f 100644 --- a/lib/grape/api/instance.rb +++ b/lib/grape/api/instance.rb @@ -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 @@ -79,9 +79,13 @@ def cascade(value = nil) end end + def compile! + LOCK.synchronize { compile } unless instance + end + # see Grape::Router#recognize_path def recognize_path(path) - LOCK.synchronize { compile } unless instance + compile! instance.router.recognize_path(path) end diff --git a/lib/grape/eager_load.rb b/lib/grape/eager_load.rb new file mode 100644 index 000000000..f7fb6c160 --- /dev/null +++ b/lib/grape/eager_load.rb @@ -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 From 32fb1d93c2fd349e3b7251bdbe32ffb86115a4ad Mon Sep 17 00:00:00 2001 From: Nicolas Klein Date: Tue, 3 Sep 2019 17:07:57 +0100 Subject: [PATCH 3/4] [AutoLoad] Addresses rubocop rules, and adds a readme line --- README.md | 18 ++++++++++++++++++ lib/grape/api.rb | 2 ++ lib/grape/eager_load.rb | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 358c9aea6..162099bb3 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,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 @@ -270,6 +281,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 diff --git a/lib/grape/api.rb b/lib/grape/api.rb index df4069a44..0387bac9c 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -112,6 +112,7 @@ def compile! end private + def instance_for_rack if never_mounted? base_instance @@ -119,6 +120,7 @@ def instance_for_rack mounted_instances.first 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 } diff --git a/lib/grape/eager_load.rb b/lib/grape/eager_load.rb index f7fb6c160..933c97908 100644 --- a/lib/grape/eager_load.rb +++ b/lib/grape/eager_load.rb @@ -15,4 +15,4 @@ Grape::API.eager_load! Grape::Presenters.eager_load! Grape::ServeFile.eager_load! -Rack::Head # AutoLoads the Rack::Head +Rack::Head # AutoLoads the Rack::Head From 782509c2c6ad36f7e0b128151e8ad0bb85d51f42 Mon Sep 17 00:00:00 2001 From: Nicolas Klein Date: Tue, 3 Sep 2019 17:09:50 +0100 Subject: [PATCH 4/4] [AutoLoad] Adds changes to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 862e7894c..09ed8972f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Features * Your contribution here. +* [#1904](https://github.com/ruby-grape/grape/pull/1904): [autoload] Allows Grape to load files on startup rather than on the first call - [@myxoh](https://github.com/myxoh). #### Fixes