I'm not sure this is the best topic name I could use but is there a way in sinatra+rack to use classes/modules like in php+apache way? I mean is there a way you can use Singleton or any other static typed variables in just request scope? What I need(would want to) is:
class Config # might be just module
# some methods that are request scoped
# cant be shared between users/reuqest
def server_settings
# possible server dependent settings
# can be shared between requests on same server
end
end
class Something
def do_thing
# I would like to access Config class here without passing it as parameter
# setted up for the current request
end
end
class MyController < Sinatra::Base
# request handling, creates Config class, setting up request parameters once
def handle_it
Something.new.do_thing
end
end
No rails here and I'm very limited on things I can do / gems I can use. MyController comes from internal framework. I can ofc extend it though. Php+apache goes from nothing every request so all your code is request only unless you use sessions/cookies.
The reason why I would like to use this is to avoid passing config instance(or any other kind of data repository) into every other instance as parameter and simply access it "globaly".
I think you looking for something like this answer Page Views counter sinatra?
tl;dr version is using set
set :my_config_property, 'hello world'
Related
I'm stumped I've tried everything I can think of to work around this and I got nothing.
When does Sinatra's request object start existing and where does it actually exist and how can I get to it from anywhere? i.e. another class that inherits from a class that inherits from a class that inherits from Sinatra::Base for example.
I've managed to get an idea of where it exists from this question but I just can't seem get any further.
Things I've tried:
def self.request
self.superclass.superclass.superclass.request
end
Various ways of changing the code execution context using `instance_eval or using:
def self.method_added method_name
a = self.new.method(method_name)
def self.define_method method_name do
a.call
end
end
and anything else I can think of but no matter what I do request is always nil, so I ask again, when and where does the request object come into existence during a request?
EDIT:
No offence but how is it hard to tell what I'm asking?
Here's the question, it's the in the title:
When and where does Sinatra's request object exist?
Sinatra has a request object, when does the object start to exist? (as in when is it not nil during the execution of code?)
When it does exist, where does it exist, within Sinatra::Base an instance of Sinatra::Base or within `Wrapper or some where else?
EDIT:
Here's what I'm doing:
in this example:
r[:action] is 'get'
r[:url] is '/'
method is get_root
instance is a variable storing self.new so I can access any instance methods.
def method_added method
return if Delegation::SIN_DSL[:basic].include?(method)
r,p = get_r_data method
m = self.instance.method(method)
self.send r[:action], r[:url].first, (#options || {}) do
(m.arity < 1 ) ? m.call : m.call(*p)
end
#options = nil
end
A sinatra app is a rack app. If you have
class MyApp < Sinatra::Base
end
then where the MyApp class gets instantiated depends on how you start up a webserver that runs it; generally speaking the rack handler (which may be of different types depending on the http server you're using) will store an instance of the sinatra app. When a rack request comes in, the server or another rack app will call the app instance with the rack env hash. Sinatra::Base#call will then do dup.call!(env), meaning that a shallow copy of the existing instance is made and then call! is invoked on the copy. The body of call! is where the request object is initialized:
def call!(env) # :nodoc:
#env = env
#request = Request.new(env)
and it's this duped app's request accessor for this instance variable that you're typically invoking when you refer to request in a route handler.
Not sure if that helps you, but it should at least answer the question.
WARNING: Answer valid for sinatra v1.4.5, but you should not expect it to remain valid. These implementation details are not part of the public sinatra API and are not documented for a reason -- you're not intended to mess with it, and doing so will quite possibly break your app if you upgrade sinatra versions. I don't recommend writing code that depends on these details.
I'm writing an REST API Wrapper ruby gem, here's how I want it to work:
# to start, initialize a client
client = APIWrapper.new "someToken"
# then you can fetch a collection of resources
client.some_resources.fetch
# you don't have to always start with `client` when you have a resource loaded
resource = client.some_resource.first
resource.do_stuff
moar_resource = resource.some_nested_resources.find({foo: 'bar'})
moar_resource.do_other_stuff
# and you can run multiple clients with different tokens
client_1 = APIWrapper.new "token 1"
client_2 = APIWrapper.new "token 2"
so the service (which is out of my control) requires an "apiToken" to be passed as a parameter in every request. So I need to pass the token in Client to each nested resources so they can work without relying on information stored in client object. (some api wrappers will always require you to start your action from client, eg. client.resources["someId"].do_stuff
What I'm doing right now is that I have a connection object, which is a Faraday instance, with apiToken set as a default parameter, in Client once it is initialized. Then every time I'm initializing a resource instance (eg. resource = client.resources.first ) I pass down the connection object, so it can use the existing connection to perform API calls.
After writing 5,6 classes I started thinking if there is a better way to do this since in every class I'm doing something like
Module APIWrapper
class Resource(options = {})
#conn = options[:connection]
#attribute_1 = options[:attribute_1]
# ... assign other attributes
end
end
I've looked at some other API Wrapper's repos but I haven't found an answer. Any suggestions?
This is the nature of Object Oriented programming.
There are hacky ways to do this in ruby, such as saving state on a module as such:
module Client
class << self
attr_accessor :connection
end
end
Client.connection = # Your faraday object, with API token
class Resource
def some_call
Client.connection.post #whatever
end
end
This is generally considered pretty ugly.
If I were you I would simply create a Resource superclass that takes the #conn in the constructor. In every instance, call the superclass's constructor.
You will still have to pass in the #conn object when you initialize a resource, but the assignment will be all in one place and DRY.
I am using sinatra and I have the code:
class App < Sinatra::Base
configure do
#logger = Logger.new "./log"
end
#logger.info "App started" #this line works
get "/info" do
#logger.info "/info inquired" #this does not work and complain #logger is nilClass
end
end
Why #logger inside get block gives a nil object? How can I use #logger in this case?
PS. If I use a class variable like ##logger, the code above works. But why the instance variable is not working in this case?
Instance variables attach themselves to whatever object is self at the time the instance variables spring into existence.
On the face of things, these are the values for self:
class App < Sinatra::Base
#In here, self=App
#When a block executes, it sees the value for self that existed in
#the surrounding scope at the time the block was CREATED:
configure do #a block
#So...in here self=App
#logger = Logger.new "./log"
end
#logger.info "App started" #this line works
get "/info" do #a block
#Similarly, in here self=App
#logger.info "/info inquired" ##logger is NilClass
end
end
Based on that state of things, you are right to be confused: it looks like when configure() executes the block that is passed to it, #logger will spring into existence and attach itself to App, then when get() calls the block that is passed to it, the #logger instance variable will refer to the instance variable attached to App.
But...ruby offers ways to change the value of self that a block sees when the block EXECUTES. Here is an example:
p = Proc.new { puts self }
p.call
class Dog
def initialize(a_proc)
#In here, self is a Dog instance
instance_eval &a_proc
end
end
Dog.new p
--output:--
main
#<Dog:0x000001009b6080>
Based on your error, you have to suspect that Sinatra must be employing some ruby tricks to change self when it executes the block passed to get().
How can we know this?
Ruby is the wild west of programming languages, so you can't ever know what is going to happen unless you look at the source code or good docs if they exist. The source code is pretty convoluted. I found this in the docs:
Some knowledge of Sinatra’s internal design is required to write good
extensions. This section provides a high level overview of the classes
and idioms at the core of Sinatra’s design.
Sinatra has two distinct modes of use that extensions should be aware
of:
The “Classic” style, where applications are defined on main / the
top-level – most of the examples and documentation target this usage.
Classic applications are often single-file, standalone apps that are
run directly from the command line or with a minimal rackup file. When
an extension is required in a classic application, the expectation is
that all extension functionality should be present without additional
setup on the application developers part (like included/extending
modules).
The “Modular” style, where Sinatra::Base is subclassed explicitly and
the application is defined within the subclass’s scope. These
applications are often bundled as libraries and used as components
within a larger Rack-based system. Modular applications must include
any desired extensions explicitly by calling register ExtensionModule
within the application’s class scope.
Most extensions are relevant to both styles but care must be taken by
extension authors to ensure that extensions do the right thing under
each style. The extension API (Sinatra.register and Sinatra.helpers)
is provided to help extension authors with this task.
Important: The following notes on Sinatra::Base and
Sinatra::Application are provided for background only - extension
authors should not need to modify these classes directly.
Sinatra::Base The Sinatra::Base class provides the context for all
evaluation in a Sinatra application. The top-level DSLish stuff exists
in class scope while request-level stuff exists at instance scope.
Applications are defined within the class scope of a Sinatra::Base
subclass. The “DSL” (e.g., get, post, before, configure, set, etc.) is
simply a set of class methods defined on Sinatra::Base. Extending the
DSL is achieved by adding class methods to Sinatra::Base or one of its
subclasses. However, Base classes should not be extended with extend;
the Sinatra.register method (described below) is provided for this
task.
Requests are evaluated within a new Sinatra::Base instance – routes,
before filters, views, helpers, and error pages all share this same
context. The default set of request-level helper methods (e.g, erb,
haml, halt, content_type, etc.) are simple instance methods defined on
Sinatra::Base or within modules that are included in Sinatra::Base.
Providing new functionality at the request level is achieved by adding
instance methods to Sinatra::Base.
As with DSL extensions, helper modules should not be added directly to
Sinatra::Base by extension authors with include; the Sinatra.helpers
method (described below) is provided for this task.
http://www.sinatrarb.com/extensions.html
You can define your own logger in your Sinatra::Base and use it in your get block by doing:
class App < Sinatra::Base
set :logger, Logger.new("./log")
helpers do
def logger; self.class.logger; end
end
logger.info self
get "/info" do
logger.info self
end
# ...
end
Or by using the class variable as you note in your edit. The log file from the above configuration shows why:
I, [2014-06-01T16:36:51.593033 #16144] INFO -- : App
I, [2014-06-01T16:36:59.438078 #16144] INFO -- : #<App:0x9aa919c #default_layout=:layout, #app=nil ...
In the first case, self is the application class, while in the get block, self is the instance of the class.
To clarify, in your example: Ruby interprets the first #logger.info (called from the context of your class) to be a class instance variable, while the second #logger.info is interpreted as an instance variable (which has not been defined). The variable you define in your configure block is set in the class context.
I have a simple class:
class Repository
class << self
def find(id)
...
end
end
end
It is called like this throughout our app:
thing = Repository.find("abc")
We are in a Sinatra/rack environment. During the request phase we do something like this:
env['org'] = 'acme'
What I'd like to do is be able to get access to 'acme' from inside the class Repository, without having to explicitly pass it in. There are so many calls to this class all over the place that it would be a pain to pass in the value each time through the find method e.g., find(id,org = nil). I thought maybe there's a way to include the rack gem in Repository, and get at it that way, but so far no luck. Global variables are out - has to be scoped to the request.
Is it possible to do something like this?
Personally, I think having a variable that changes like that inside a class method is asking for trouble, it breaks the Law of Demeter by reaching across boundaries. Instead, I'd wrap it in a Sinatra helper which then passes the second argument by default.
helpers do
def find( s )
Repository.find( s, env['org'] )
end
end
and modify the Repository's find method to take the second argument.
I'm little new to Ruby, but I have the following coded using Grape API in Ruby. I have #data = YAML.load() called every time it hits GET /api/v1/foo, is there a way in Grape to only load it once and use it? This way is more optimized and not calling YAML.load() at every time. Should I override the initialize method and put a super() for this operation?
Thanks,
require 'grape'
require 'json'
require "yaml"
module MyProject
CONFIG_FILE = "./config.yml"
class Api < Grape::API
rescue_from :all
prefix 'api'
version 'v1'
format :json
resources :foo do
get do
#data = YAML.load(File.open(MyProject::CONFIG_FILE))
end
end
end
end
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
end
end
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
end
end
Not tested, but with this way, you ensure that the data is only loaded once, when your Api class is loaded