Использование Sinatra для больших проектов через несколько файлов

Кажется, что в Sinatra все обработчики маршрутов записываются в один файл, если я правильно понимаю, он действует как один большой/маленький контроллер. Есть ли способ разделить его на отдельные независимые файлы, поэтому, если скажем, что кто-то называет "/" - выполняется одно действие, и если smth нравится "/posts/2", то другое действие - аналогичная логика, применяемая в PHP

Ответ 1

Вот базовый шаблон для приложений Sinatra, которые я использую. (В моих более крупных приложениях было создано более 200 файлов, не считая драгоценных камней вендоров, охватывающих 75-100 явных маршрутов. Некоторые из этих маршрутов - это маршруты регулярного выражения, содержащие дополнительные 50+ шаблонов маршрута.) При использовании Thin вы запускаете приложение, как это, используя:
thin -R config.ru start

Изменить. Я поддерживаю свой собственный Monk скелет на основе нижеуказанного Riblits. Чтобы использовать его для копирования моего шаблона в качестве основы для ваших собственных проектов:

# Before creating your project
monk add riblits git://github.com/Phrogz/riblits.git

# Inside your empty project directory
monk init -s riblits

Макет файла:

config.ru
app.rb
helpers/
  init.rb
  partials.rb
models/
  init.rb
  user.rb
routes/
  init.rb
  login.rb
  main.rb
views/
  layout.haml
  login.haml
  main.haml

 
config.ru

root = ::File.dirname(__FILE__)
require ::File.join( root, 'app' )
run MyApp.new

 
app.rb

# encoding: utf-8
require 'sinatra'
require 'haml'

class MyApp < Sinatra::Application
  enable :sessions

  configure :production do
    set :haml, { :ugly=>true }
    set :clean_trace, true
  end

  configure :development do
    # ...
  end

  helpers do
    include Rack::Utils
    alias_method :h, :escape_html
  end
end

require_relative 'models/init'
require_relative 'helpers/init'
require_relative 'routes/init'

 
хелперы /init.rb

# encoding: utf-8
require_relative 'partials'
MyApp.helpers PartialPartials

require_relative 'nicebytes'
MyApp.helpers NiceBytes

 
хелперы /partials.rb

# encoding: utf-8
module PartialPartials
  def spoof_request(uri,env_modifications={})
    call(env.merge("PATH_INFO" => uri).merge(env_modifications)).last.join
  end

  def partial( page, variables={} )
    haml page, {layout:false}, variables
  end
end

 
хелперы /nicebytes.rb

# encoding: utf-8
module NiceBytes
  K = 2.0**10
  M = 2.0**20
  G = 2.0**30
  T = 2.0**40
  def nice_bytes( bytes, max_digits=3 )
    value, suffix, precision = case bytes
      when 0...K
        [ bytes, 'B', 0 ]
      else
        value, suffix = case bytes
          when K...M then [ bytes / K, 'kiB' ]
          when M...G then [ bytes / M, 'MiB' ]
          when G...T then [ bytes / G, 'GiB' ]
          else            [ bytes / T, 'TiB' ]
        end
        used_digits = case value
          when   0...10   then 1
          when  10...100  then 2
          when 100...1000 then 3
          else 4
        end
        leftover_digits = max_digits - used_digits
        [ value, suffix, leftover_digits > 0 ? leftover_digits : 0 ]
    end
    "%.#{precision}f#{suffix}" % value
  end
  module_function :nice_bytes  # Allow NiceBytes.nice_bytes outside of Sinatra
end

 
модели /init.rb

# encoding: utf-8
require 'sequel'
DB = Sequel.postgres 'dbname', user:'bduser', password:'dbpass', host:'localhost'
DB << "SET CLIENT_ENCODING TO 'UTF8';"

require_relative 'users'

 
модели /user.rb

# encoding: utf-8
class User < Sequel::Model
  # ...
end

 
<Сильные > маршруты /init.rb

# encoding: utf-8
require_relative 'login'
require_relative 'main'

 
<Сильные > маршруты /login.rb

# encoding: utf-8
class MyApp < Sinatra::Application
  get "/login" do
    @title  = "Login"
    haml :login
  end

  post "/login" do
    # Define your own check_login
    if user = check_login
      session[ :user ] = user.pk
      redirect '/'
    else
      redirect '/login'
    end
  end

  get "/logout" do
    session[:user] = session[:pass] = nil
    redirect '/'
  end
end

 
<Сильные > маршруты /main.rb

# encoding: utf-8
class MyApp < Sinatra::Application
  get "/" do
    @title = "Welcome to MyApp"        
    haml :main
  end
end

 
вид /layout.haml

!!! XML
!!! 1.1
%html(xmlns="http://www.w3.org/1999/xhtml")
  %head
    %title= @title
    %link(rel="icon" type="image/png" href="/favicon.png")
    %meta(http-equiv="X-UA-Compatible" content="IE=8")
    %meta(http-equiv="Content-Script-Type" content="text/javascript" )
    %meta(http-equiv="Content-Style-Type" content="text/css" )
    %meta(http-equiv="Content-Type" content="text/html; charset=utf-8" )
    %meta(http-equiv="expires" content="0" )
    %meta(name="author" content="MeWho")
  %body{id:@action}
    %h1= @title
    #content= yield

Ответ 2

Совершенно верно. Чтобы увидеть пример этого, я рекомендую загрузить камень Monk, описанный здесь:

https://github.com/monkrb/monk

Вы можете 'gem install' его через rubygems.org. После того, как у вас есть драгоценный камень, сгенерируйте пример приложения, используя приведенные выше инструкции.

Обратите внимание, что вам не нужно использовать Monk для вашей реальной разработки, если вы этого не хотите (на самом деле, я думаю, что это может быть не актуально). Дело в том, чтобы увидеть, как вы можете легко структурировать свое приложение в стиле MVC (с отдельными файлами маршрутов, подобными контроллеру), если вы хотите.

Это довольно просто, если вы посмотрите на то, как Monk обрабатывает его, в основном вопрос о необходимости файлов в отдельных каталогах, что-то вроде (вам нужно определить root_path):

Dir[root_path("app/**/*.rb")].each do |file|
    require file
end

Ответ 3

Сделайте поиск Google для "шаблона Sinatra", чтобы получить некоторые идеи о том, как другие излагают свои приложения Sinatra. Из этого вы, вероятно, можете найти тот, который соответствует вашим потребностям или просто сделать свой собственный. Это не слишком сложно сделать. Когда вы разрабатываете больше приложений Sinatra, вы можете добавить к своему шаблону.

Вот что я сделал и использовал для всех моих проектов:

https://github.com/rziehl/sinatra-boilerplate

Ответ 4

Я знаю, что это старый запрос, но я до сих пор не могу поверить, что никто не упоминал Padrino. Вы можете использовать его в качестве основы для вершине Синатры, или по частям, добавляя только драгоценные камни, которые вас интересуют. Он пинает десять задниц задницы!

Ответ 5

Ключом к модульности Sinatra для более крупных проектов является изучение использования основных инструментов.

SitePoint имеет очень хорошее хорошее руководство, из которого вы можете видеть модульные приложения и помощники Sinatra. Однако вы должны обратить особое внимание на одну важную деталь. Вы сохраняете несколько приложений Sinatra и монтируете их с помощью Rackup. Как только вы знаете, как написать базовое приложение, посмотрите файл config.ru этого учебника и посмотрите, как они монтируют независимые приложения Sinatra.

Как только вы научитесь запускать Sinatra со стойкой, откроется новый мир стратегий модульности. Это, очевидно, предлагает попробовать что-то действительно полезное: теперь вы можете полагаться на наличие отдельных Gems для каждого дополнительного приложения, что может позволить вам легко модифицировать ваши модули.

Не стоит недооценивать возможности использования gem-модулей для вашего приложения. Вы можете легко протестировать экспериментальные изменения в хорошо разграниченной среде и легко развернуть их. В равной степени легко вернуться назад, если что-то пойдет не так.

Существует тысячи способов организовать ваш код, поэтому не мешало бы получить макет, похожий на Rails. Однако есть и некоторые отличные сообщения о том, как настроить свою собственную структуру. Эта публикация охватывает другие частые потребности большинства веб-разработчиков.

Если у вас есть время, я рекомендую вам узнать больше о Rack, общей для любого веб-приложения на основе Ruby. Это может оказать гораздо меньшее влияние на то, как вы выполняете свою работу, но всегда есть определенные задачи, которые большинство людей делают в своих приложениях, которые лучше подходят для промежуточного ПО Rack.

Ответ 6

Мой подход к размещению разных проектов на одном сайте - это использование sinatra/namespace таким образом:

server.rb

require "sinatra"
require "sinatra/namespace"

if [ENV["LOGNAME"], ENV["USER"]] == [nil, "naki"]
    require "sinatra/reloader"
    register Sinatra::Reloader
    set :port, 8719
else
    set :environment, :production
end

for server in Dir.glob "server_*.rb"
    require_relative server
end

get "/" do
    "this route is useless"
end

server_someproject.rb

module SomeProject
    def self.foo bar
       ...
    end
    ...
end

namespace "/someproject" do
    set :views, settings.root
    get "" do
        redirect request.env["REQUEST_PATH"] + "/"
    end
    get "/" do
        haml :view_someproject
    end
    post "/foo" do
        ...
        SomeProject.foo ...
    end
end

view_someproject.haml

!!!
%html
    ...

Другая информация о подпроектах, которые я использовал, заключалась в том, чтобы добавить их имена, описания и маршруты к какой-либо глобальной переменной, которая используется "/" для создания главной страницы руководства, но сейчас у меня нет фрагмента.

Ответ 7

Когда Монк не работал у меня, я сам начал работать над шаблонами.

Если вы думаете об этом, нет ничего особенного в связывании набора файлов. Философия монаха была объяснена мне в начале 2011 года во время RedDotRubyConf, и они специально сказали мне, что действительно необязательно использовать ее, особенно сейчас, когда ее почти не поддерживают.

Это хорошее начало для тех, кто хочет использовать ActiveRecord:

Простая Sinatra MVC

https://github.com/katgironpe/simple-sinatra-mvc

Ответ 8

Чтение документов здесь:

Расширения Sinatra

Похоже, что Sinatra позволяет разложить ваше приложение на Ruby Modules, которое можно втянуть через метод "register" Sinatra или "helpers", например:

helpers.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Helpers

      def require_logged_in()
        redirect('/login') unless session[:authenticated]
      end

    end
  end
end

маршрутизации /foos.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Routing
      module Foos

        def self.registered(app)           
          app.get '/foos/:id' do
            # invoke a helper
            require_logged_in

            # load a foo, or whatever
            erb :foos_view, :locals => { :foo => some_loaded_foo }
          end   
        end  

      end
    end     
  end
end

app.rb

#!/usr/bin/env ruby

require 'sinatra'

require_relative 'routing/foos'

class SampleApp < Sinatra::Base

  helpers Sinatra::Sample::Helpers

  register Sinatra::Sample::Routing::Foos

end