16. Web с Ruby. Sinatra

16. Web с Ruby. Sinatra

16. Web с Ruby. Sinatra

18 декември 2013

Днес

Преди това

въпроси около метапрограмиране и обектния модел в Ruby

Споделете поне три различни начина за дефиниране инстанционни методи в клас.

  • def something; end
  • define_method :something { |args| }
  • eval()

Въпрос 2

Какво прави instance_eval?

instance_eval променя self в рамките на един блок

Въпрос 3

Ако искате да напишете клас-прокси, кой ще бъде родителският ви клас?

  • BasicObject
  • Защо не Object?
  • В Object има твърде много методи, за разлика от BasicObject

Въпрос 4

Как могат да се създават анонимни класове и/или модули?

Посредством Class.new { block } и Module.new { block }

Въпрос 5

Що е то singleton class на обект? Какво знаете за него?

  • За всеки обект при нужда се създава специален клас, наречен singleton class
  • Съдържа методите, добавени специално към този обект
  • Известен е още като metaclass, eigenclass и собствен клас
  • Symbol и Fixnum нямат собствени класове

Въпрос 6

1. Къде се пазят инстанционните методи?

В модули (класове).

2. Къде се пазят инстанционните методи, уникални за дадена инстанция?

В специално създадения за нея singleton клас.

Въпрос 7

1. Къде се пазят класовите методи? Защо?

В singleton класа на текущия клас. Защото всеки клас е обект (инстанция) от тип Class и всички "класови" методи, които има, са всъщност инстанционни, уникални за тази конкретна инстанция на Class.

2. Ако имаме class A; end и class B < A; end, то какво е B.singleton_class.superclass?

Родителският клас на B.singleton_class е A.singleton_class.

Уеб с Ruby

Как работи?

Rack

Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.

Rack в GitHub: rack/rack

Примерно Rack-приложение

Запазете следния код във файл config.ru и пуснете с rackup config.ru:

hello_world_app = proc do |env|
  [200, {'Content-Type'   => 'text/plain'}, ['Hello, World!']]
end

run hello_world_app

Предполага се, че сте си инсталирали библиотеката rack. Можете да направите това с gem install rack.

Вход

Вход

нека го инспектираме

dump_env = proc do |env|
  body = env.map { |key, value| "#{key}: #{value}" }.join("\n")
  [200, {'Content-Type'   => 'text/plain'}, [body]]
end

run dump_env

Вход

SERVER_SOFTWARE: thin 1.3.1 codename Triple Espresso
SERVER_NAME: localhost
rack.input: #<Rack::Lint::InputWrapper:0x007fbe7b8484b0>
rack.version: [1, 0]
rack.errors: #<Rack::Lint::ErrorWrapper:0x007fbe7b848438>
rack.multithread: false
rack.multiprocess: false
rack.run_once: false
REQUEST_METHOD: GET
REQUEST_PATH: /
PATH_INFO: /
REQUEST_URI: /
HTTP_VERSION: HTTP/1.1
HTTP_HOST: localhost:9292
HTTP_CONNECTION: keep-alive
HTTP_CACHE_CONTROL: max-age=0
HTTP_USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11
HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_ENCODING: gzip,deflate,sdch
HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.8
HTTP_ACCEPT_CHARSET: ISO-8859-1,utf-8;q=0.7,*;q=0.3
GATEWAY_INTERFACE: CGI/1.2
SERVER_PORT: 9292
QUERY_STRING:
SERVER_PROTOCOL: HTTP/1.1
rack.url_scheme: http
SCRIPT_NAME:
REMOTE_ADDR: 127.0.0.1
async.callback: #<Method: Thin::Connection#post_process>
async.close: #<EventMachine::DefaultDeferrable:0x007fbe7b8443b0>

Изход

The Rack lobster

Rack-посредници

= Rack middleware

Rack-посредници

примери

Поддържани уеб сървъри

Mongrel               Ebb
EventedMongrel        Fuzed
SwiftipliedMongrel    Glassfish v3
WEBrick               Phusion Passenger
FCGI                  Puma
CGI                   Rainbows!
SCGI                  Unicorn
LiteSpeed             unixrack
Thin                  Zbatery

Поддържани уеб библиотеки

Ruby on Rails   Coset
Padrino         Halcyon
Sinatra         Mack
Sin             Maveric
Vintage         Merb
Waves           Camping
Wee             Ramaze

…и много други.

Deployment на production

В development режим

Ruby (App)

или къде обикновено пишем нашия уеб код

Sinatra

Sinatra is a domain-specific language for building websites, web services, and web applications in Ruby. It emphasizes a minimalistic approach to development, offering only what is essential to handle HTTP requests and deliver responses to clients.

— Из "Sinatra Up and Running"

Официален сайт: sinatrarb.com, GitHub проект: sinatra/sinatra

Sinatra

Sinatra

не е фреймуърк

Sinatra vs. Padrino vs. Rails

Обратно към Sinatra

инсталация

Модели на разработка

Класически модел

Модулен модел

Hello, World!

пример в класически стил

require 'sinatra'

get '/' do
  'Booyah!'
end

Пускате с ruby booyah.rb и тествате, например с telnet

Какво става, ако поискаме несъществуващ адрес?

Грешки: 404

Грешки

Маршрути (routes)

Параметри

Параметри в URL-а

get '/greet/:name' do
  "Hey there, #{params[:name]}!"
end

Wildcard маршрути и приоритет

Камък, ножица, хартия

малко по-голям пример

Камък, ножица, хартия (2)

require 'sinatra'

before do
  content_type :txt
  @moves = {rock: :scissors, paper: :rock, scissors: :paper}
end

get '/play/:move' do
  # Игрова логика
end

Камък, ножица, хартия (3)

get '/play/:move' do
  # Внимавайте с #to_sym на произволни низове!
  player_move = params[:move].to_sym

  unless @moves.has_key?(player_move)
    valid_moves = @moves.keys.map(&:to_s).join(', ')
    halt 403, "You must throw one of the following: #{valid_moves}"
  end
end

Камък, ножица, хартия (4)

get '/play/:move' do
  # Проверка за валиден ход...

  computer_move = @moves.keys.sample

  if player_move == computer_move
    "Oh, my!\nIt's a tie!"
  elsif computer_move == @moves[player_move]
    "Nicely done, #{player_move} beats #{computer_move}!"
  else
    "Ouch... You got trashed, #{computer_move} beats #{player_move}. Better luck next time!"
  end
end

Филтри

Rack middleware в Sinatra

Контекст на оценка на маршрутите

halt, pass и redirect

Статични файлове

Динамични изгледи

още темплейти

Динамични изгледи

пример

get '/' do
  # Ще върне низ с рендерирания Erb темплейт, кръстен "index"
  erb :index
end

Рендериране

Вградени темплейти

Вградени темплейти

get '/' do
  erb :index
end

__END__

@@index
<h1>Hey, there!</h1>
<p>The time now is <%= Time.now %></p>

Външни темплейти

Предаване на данни към темплейтите

Предаване на данни към темплейтите

get '/posts/:id' do
  @info = "This is post ID #{params[:id]}"
  erb :post, locals: {title: 'My Blog'}
end

__END__

@@post
<h1><%= title %></h1>
<p><%= @info %></p>

Прихващане на грешки

Конфигурационният блок

Environment

Конфигурационен блок

configure do
  # Важи за всички обкръжения
  set :public_folder, File.expand_path('../assets', __FILE__)
  set :my_custom_var, 'bar'
end

configure :production do
  # Важи само за "production" обкръжение
  set :public_folder, '/var/www/assets'
end

Конфигурационен блок

headers, request и response

Методи на request (1)

request.accept              # ['text/html', '*/*']
request.accept? 'text/xml'  # true
request.preferred_type(t)   # 'text/html'
request.body                # request body sent by the client (see below)
request.scheme              # "http"
request.script_name         # "/example"
request.path_info           # "/foo"
request.port                # 80
request.request_method      # "GET"
request.query_string        # ""
request.content_length      # length of request.body
request.media_type          # media type of request.body
request.host                # "example.com"

Методи на request (2)

request.get?                # true (similar methods for other verbs)
request.form_data?          # false
request["SOME_HEADER"]      # value of SOME_HEADER header
request.referrer            # the referrer of the client or '/'
request.user_agent          # user agent (used by :agent condition)
request.cookies             # hash of browser cookies
request.xhr?                # is this an ajax request?
request.url                 # "http://example.com/example/foo"
request.path                # "/example/foo"
request.ip                  # client IP address
request.secure?             # false (would be true over ssl)
request.forwarded?          # true (if running behind a reverse proxy)
request.env                 # raw env hash handed in by Rack

Бисквитки

cookies

Сесии

Кеширане

Стрийминг

Краен стрийминг

пример

get '/stream' do
  stream do |connection|
    connection << "Start of the stream\n"
    sleep 1
    connection << "Some more data\n"
    sleep 1
    connection << "And even more data\n"
  end
end

Безкраен стрийминг

пример

subscribers = []
get '/listen' do
  stream :keep_open do |subscriber|
    subscriber << "Welcome!\n"
    subscribers << subscriber
  end
end

get '/broadcast/:message' do
  message = params[:message]
  subscribers.each do |subscriber|
    subscriber << "#{Time.now}: #{message}\n"
  end
  "Message #{message} sent.\n"
end

Модулен стил

Модулен стил

работещ пример

require 'sinatra/base'

class Welcome < Sinatra::Base
  get '/' do
    'Welcome to modular Sinatra!'
  end

  run!
end

Модулен стил

псевдо-пример

class Blog < Sinatra::Base
  set :root, 'some/path'

  Article.each do |article|
    get "/#{article.slug}" do
      erb :post, locals: {article: article}
    end
  end

  get '/' do
    @articles = Article.all_sorted_by_publish_date
    erb :index
  end
end

sinatra-contrib

Collection of common Sinatra extensions, semi-officially supported.

GitHub repo: sinatra/sinatra-contrib

Повече информация

Въпроси