Loading attributes only once in Grape
The short answer is that Grape
doesn't work quite how you think, and attribute variables of the MyProject::Api
are not the way forward for your new web service. However, it is an interesting question, and worth exploring why this is so.
If you add a puts self.inspect
inside the resources :foo
block, and run using rackup
, when you call the route you should see that self
is in fact a Grape::Endpoint
object. Also, no matter what you try to do with instance variables, they will always start in the same state for each request. That is because Grape
turns your route definitions into prepared Grape::Endpoint
objects, with a lot of the definition data and setup put into a quickly-accessible form (so that it is not figured out on each request). Eventually, on each request, the matching Grape::Endpoint
object including your block (and other details you defined for the route) is duplicated before being called, meaning that state is not maintained between requests.
This may seem complicated, but most frameworks covering web service requests will do something similar. Generally you don't want request-handling state to persist between requests. Frameworks with larger scope - e.g. Rails - have places to put more persistent data planned out for you. Grape does not have this defined, which has its pros and cons. One obvious plus point is that you are more free to use whatever other data persistence approach that you wish to.
23tux's answer will sort you out immediately for loading config. Although I'm not entirely sure how @@data
becomes accessible to the endpoint block (it may even be creating a closure around the variable).
Longer term, you should look to moving config management out of your MyProject::Api
class, and including it as a module via Grape's helpers
method (I'm happy to provide an example if you are interested).
Edit: Example based on your current code, but moving config management to a separate module:
require 'grape'require 'json'require "yaml"module MyProject module Config CONFIG_FILE = "./config.yml" @@data = nil def config @@data ||= YAML.load( File.open( CONFIG_FILE ) ) end end class Api < Grape::API rescue_from :all prefix 'api' version 'v1' format :json helpers MyProject::Config resources :foo do get do config end end endend
This is one step further, structurally, than 23tux's answer, but is still not completely separating concerns of storage (and caching etc) versus api access. As you progress towards a more sophisticated web service, you will want to keep the Grape route definitions simple, with only a small amount of logic that manages or manipulates the data - well, at least as seen directly in the blocks.
One way to link between your Grape definitions, and other gems that might manage config, logging, authentication and other services, is via Grape's helpers
method. Grape also has some built-in helper methods for common tasks.
The main exception to using helpers MyModule
to add shared functions into Grape is when you want to manage displaying data objects (aka "models") from your core application. For that you have a few choices, but the grape-entity
gem and the present
method is not a bad place to start.
If the @data
is the same for the whole api, and doesn't change at any time, just use a class variable
require 'grape'require 'json'require "yaml"module MyProject CONFIG_FILE = "./config.yml" class Api < Grape::API @@data = YAML.load(File.open(MyProject::CONFIG_FILE)) rescue_from :all prefix 'api' version 'v1' format :json resources :foo do get do puts @@data end end endend
Not tested, but with this way, you ensure that the data is only loaded once, when your Api
class is loaded