In my sinatra application, I would like to create some methods for manipulating the session. I would like to declare all of those methods in a module, and have that module outside any routes. For example:
enable :sessions
module SessionEditing
def setup
session[:value] = "something"
end
end
get "/" do
SessionEditing.setup
redirect "/test"
end
get "/otherRoute" do
SessionEditing.setup
redirect "/test"
end
get "/test"
puts session[:value] #=> "something"
end
I thought that there might be a way to elevate the scope of session, but I haven't been able to find out how. I also have learned that passing session into the method doesn't work because you can't set it back, even if the method returns a new session setting session = SessionEditing.setup(session) will not actually make the session change. How can I get this to work?
Calling SessionEditing.setup(session) will allow you to modify the session. The problem is `SessionEditing is incorrectly implemented. Try:
module SessionEditing
def self.setup
session[:value] = "something"
end
end
When you call SessionEditing.setup, you are calling the module method .setup, which is why you need to define the method with self, as it defines setup as a class method.
Related
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'
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 have an util method into a Sinatra application and I would like to tested from my TestCase.
The problem is that I don't know how to invoke it, if I just use app.util_method I have the error NameError: undefined local variable or method 'util_method' for #<Sinatra::ExtendedRack:0x007fc0c43305b8>
my_app.rb:
class MyApp < Sinatra::Base
# [...] routes methods
# utils methods
def util_method
return "hi"
end
end
my_app_test.rb:
require "my_app.rb"
require "test/unit"
require "rack/test"
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
# [...] routes methods tests
def test_util_method
assert_equal( "hi", app.util_method )
end
end
Sinatra aliases the new method to new! before redefining it, so the simplest solution is to use that instead:
def app
MyApp.new!
end
Of course I only noticed that after I’d come up with the following, which I’ll leave in as it could be useful/informative.
A possible way to get round Sinatra redefining the new method and returning a complete Rack app a get hold of an instance your actual base class is to do what the “real” new method does yourself:
def app
a = MyApp.allocate
a.send :initialize
a
end
This is a bit of a hack, but it might be useful for testing.
Another technique would be to “walk” the middleware stack until you got to your class. The following is a little fragile, as it depends on all the middleware involved to use the name #app to refer to the next app in the stack, but this is fairly common.
def app
a = MyApp.new
while a.class != MyApp
a = a.instance_variable_get(:#app)
end
a
end
That won’t work on the yet to be released Sinatra 1.4 though (at least not on the current master, which is commit 41840746e866e8e8e9a0eaafc53d8b9fe6615b12), as new now returns a Wrapper class and the loop never ends. In this case you can grab the base class directly from the #instance variable:
def app
MyApp.new.instance_variable_get :#instance
end
(note this last technique may well change before the final 1.4 release).
The problem you are encountering is, that MyApp.new does not return an instance of MyApp but an instance of the middleware wrapping your App (usually Rack::Head or Sinatra::ShowExceptions). A good explanation can be found in the thread Sinatra Usage Question / Rack App.
The only solution I can think of is to change your instance method to a class method which can be called without the instance itself. As the instance of your App may be freshly instantiated for every request, an instance method probably doesn't have much advantages over a class method in your scenario.
Edit:
In the upcoming Sinatra 1.4 the initialization will change. Sinatra::Base.new will return a Sinatra::Wrapper instance, which exposes #settings and #helpers. This may help solve the problem of accessing Sinatra::Base instance methods. See the Sinatra Changelog for more information.
I'm using Sinatra (1.2) and RSpec (2.5) and would like to create a new object with an attribute TDD style. This is how the end result should look like:
class User
def initialize(name)
#name = name
end
end
I know I have to write the example before the implementation but I'm trying to explain my question here. :) Here is the not working spec I have so far:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
When I run RSpec I get the "expected: 1 time, received 0 times" error. Any idea how I can explain RSpec I would like to assign the name attribute?
Note: I'm not using Rails, not using ActiveRecord or anything, just Ruby.
First of all, let me explain why the spec you have written doesn't work:
You set an expectation that the mock object returned by mock(User) should receive name=. There are two problems with this. First of all the mock will receive nothing, because it is never called. mock(User) returns a mock object, and it cannot be used to set expectations for what the User class object will receive (to do that simply do User.should_receive(...)). Secondly, even if you had set the expectation on the User class object, that object will never receive name=. There are two reasons for this, too: firstly because name= (had it existed) would be an instance method, and as such not called on the class object, and secondly, you declare no name= instance method. What your code does is that it sets an instance variable.
Now, how should you write a test for this? You shouldn't. Tests are to define and assert the behaviour, not the implementation. Setting an instance variable is pure implementation. There is in your example code no way to get the value of the #name instance variable from outside of the class, therefore there is no reason to write a test for it.
Obviously your code is just an example, anything useful would do something with the #name variable, and that is what you should test. Start by writing a test for what a User object will be used for, then write all the implementation needed to fulfill that test (but no more). Write a test that reflects how the object will be used in actual production code.
I'd really recommend you don't approach this using mocks. It's not what they're for. In fact, specifying getters/setters like this is not really what TDD is for. The idea is to let a requirement drive the setters/getters into existence. For example, there might be a requirement that the user's name appear in a welcome message when he/she logs in. Then you might do something like this:
describe 'login process' do
it "displays user's name after successful login" do
user = User.new("Cimm", "cimm#somewhere.com", "secret")
post "/login", :email => "cimm#somewhere.com", :password => "secret"
last_response.body.should =~ /Welcome Cimm/m
end
end
This specifies behavior, and forces you to implement a means of setting the name attribute (via the constructor, in this case) and a means of accessing it. No need to specify the constructor directly.
Do you really want to mock the very object you are developing?
require 'rspec'
class User
attr_accessor :name
def initialize(name)
#name = name
end
end
describe User do
subject {User.new "other name"}
it "creates a new user object" do
subject.should respond_to :name=
end
end
Here's the situation:
I have a User model, and two modules for authentication: Oauth and Openid. Both of them override ActiveRecord#save, and have a fair share of implementation logic.
Given that I can tell when the user is trying to login via Oauth vs. Openid, but that both of them have overridden save, how do "finally" override save such that I can conditionally call one of the modules' implementations of it?
Here is the base structure of what I'm describing:
module UsesOauth
def self.included(base)
base.class_eval do
def save
puts "Saving with Oauth!"
end
def save_with_oauth
save
end
end
end
end
module UsesOpenid
def self.included(base)
base.class_eval do
def save
puts "Saving with OpenID!"
end
def save_with_openid
save
end
end
end
end
module Sequencer
def save
if using_oauth?
save_with_oauth
elsif using_openid?
save_with_openid
else
super
end
end
end
class User < ActiveRecord::Base
include UsesOauth
include UsesOpenid
include Sequencer
end
I was thinking about using alias_method like so, but that got too complicated, because I might have 1 or 2 more similar modules. I also tried using those save_with_oauth methods (shown above), which almost works. The only thing that's missing is that I also need to call ActiveRecord::Base#save (the super method), so something like this:
def save_with_oauth
# do this and that
super.save
# the rest
end
But I'm not allowed to do that in ruby.
Any ideas for a clever solution to this?
Is that what alias_method_chain would do? I've avoided that because people seemed to say it was a bad idea.
(Finding things as I go):
Alias Method Chain the Ruby Way
Yes alias method chain would help you in this situation.
But consider using delegate pattern. Original save method would trigger a callback on special delegate object (which can be as well nil) and it would do whatever needs to be done when saving user.
Also there is simliar pattern supported directly by actve record called Observer, try to read somethng about it maybe that's a good solution too.
I'm not saying this chaining methods is wrong, but there are cleaner ways to achieve what you want.