How Does Sinatra Define and Invoke the Get Method

How does Sinatra define and invoke the get method?

It is spread out in a few places, but if you look in lib/sinatra/main.rb, you can see this line at the bottom:
include Sinatra::Delegator

If we go into lib/sinatra/base.rb we see this chunk of code around like 1470.

  # Sinatra delegation mixin. Mixing this module into an object causes all
# methods to be delegated to the Sinatra::Application class. Used primarily
# at the top-level.
module Delegator #:nodoc:
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
Delegator.target.send(method_name, *args, &block)
end
private method_name
end
end

delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
:before, :after, :error, :not_found, :configure, :set, :mime_type,
:enable, :disable, :use, :development?, :test?, :production?,
:helpers, :settings
class << self
attr_accessor :target
end

self.target = Application
end

This code does what the comment says: if it is included, it delegates all calls to the list of delegated methods to Sinatra::Application class, which is a subclass of Sinatra::Base, which is where the get method is defined. When you write something like this:

require "sinatra"

get "foo" do
"Hello World"
end

Sinatra will end up calling the get method on Sinatra::Base due to the delegation it set up earlier.

Definition of the get method in Sinatra (Ruby)

As Simone pointed out, parentheses are optional in ruby, so your get method can be called either with or without the parentheses.

It looks like "get" is defined optionally as {} or &block, why is that so?

The get function takes three parameters. The first parameter is the path, the second is a Hash with a default value of empty, and the third is a block.

Blocks in ruby can be written as {} or as do...end. So in both of your examples you are passing a block to the get function

Method definition in ruby (like sinatra)

The answers can be found here: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb

When you do Sinatra in its simple mode, all methods like get or set or post are delegated through Sinatra::Delegator, which is defined here: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1977 and is mixed into global scope inside main.rb

Leaving all Sinatra tricks alone, for your own module you can achieve the effect with really simple code:

module MyMixin
def testme
puts 'testme'
end
end

extend MyMixin

testme # => testme

In ruby with sinatra, How to get I response with get method on rest client?

Method 'get' from the 'RestClient' class return some object with attributes. So response have few values. Which of them do you need? Access to them you can get by their names, its described here.


In your case, after response = RestClient.get get_url... you should have variable response and ability to call response.headers, response.code or response.body.


But im afraid that you have some problems with autorization, which means that imp_uid or token is not correct. Thats why remote server sended to you responce with http-code 401 (Unauthorized). If it is so you should try to check your imp_uid and token. If everything is correct try to reach support of iamport.kr .

how can I override sinatra's get method?

The get method adds a route handler to the Sinatra app; it's only used for building the app during startup. When the app is up and running, the incoming HTTP requests no longer call get, they call the internally stored proc instead. (And the same goes for post etc.)

Based on your description, it looks like you want a before handler - its contents will be run for every incoming HTTP request before the actual route handlers are run.

So try replacing the def get ... end block in your code with the following:

before do
puts "URL: #{request.url} AT: #{Time.now}"
end

Then again, if you just want to log the incoming requests to the console, the easiest way is to say enable :logging inside your class definition. See more detail here: http://www.sinatrarb.com/configuration.html

Calling a Sinatra app instance method from TestCase

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).

Calling Sinatra from within Sinatra

I was able to hack something up by making a quick and dirty rack request and calling the Sinatra (a rack app) application directly. It's not pretty, but it works. Note that it would probably be better to extract the code that generates this resource into a helper method instead of doing something like this. But it is possible, and there might be better, cleaner ways of doing it than this.

#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'

get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')

resource.upcase
end

get '/otherresource' do
"test"
end

If you want to know more about what's going on behind the scenes, I've written a few articles on the basics of Rack you can read. There is What is Rack? and Using Rack.



Related Topics



Leave a reply



Submit