Używanie Sinatry do większych projektów za pomocą wielu plików

184

Wygląda na to, że w Sinatrze wszystkie procedury obsługi tras są zapisywane w jednym pliku, jeśli dobrze rozumiem, działa jak jeden duży / mały kontroler. Czy jest jakiś sposób na podzielenie go na osobne niezależne pliki, więc powiedzmy, że ktoś wywołuje „/” - wykonywana jest jedna akcja, a jeśli odbierane jest coś takiego jak „/ posts / 2”, to inna akcja - podobna logika stosowana w PHP ?

klucz kosmiczny
źródło

Odpowiedzi:

394

Oto podstawowy szablon dla aplikacji Sinatra, których używam. (Moje większe aplikacje mają ponad 200 plików podzielonych w ten sposób, nie licząc klejnotów sprzedawców, obejmujących 75-100 wyraźnych tras. Niektóre z tych tras są trasami Regexp obejmującymi dodatkowe 50+ wzorców tras.) Korzystając z cienkiej, uruchamiasz aplikacja taka jak ta:
thin -R config.ru start

Edycja : Teraz utrzymuję własny szkielet Mnicha oparty na poniżej zwanych Riblits . Aby użyć go do skopiowania mojego szablonu jako podstawy własnych projektów:

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

# Inside your empty project directory
monk init -s riblits

Układ pliku:

config.ru
app.rb
pomocnicy /
  init.rb
  częściowe. rb
modele /
  init.rb
  user.rb
trasy /
  init.rb
  login.rb
  main.rb
wyświetlenia/
  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'

 
helpers / init.rb

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

require_relative 'nicebytes'
MyApp.helpers NiceBytes

 
helpers / partss.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

 
helpers / 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

 
modele / 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'

 
modele / user.rb

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

 
trasy / init.rb

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

 
trasy / 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

 
trasy / main.rb

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

 
views / 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
Phrogz
źródło
11
Szczególnie miłą rzeczą powyższym struktura specyficznie oddanie require "sequel"i DBinicjalizacji models/init.rb, a przy użyciu require_relativedla wszystkich plików, jest to, że można dysk CD do modelskatalogu, otwórz konsolę IRB oraz rodzaj require './init'i masz swój pełny konfiguracja bazy danych i model załadowany do interaktywnej eksploracji .
Phrogz
1
Świetna przykładowa struktura, idealna dla Sinatry noob, takiej jak ja, na zdrowie.
Barry Jordan
27
Użyłem innego podejścia. Koduj całą logikę biznesową, taką jak użytkownicy i usługi, w języku ruby, bez konieczności używania „sinatra”. To sprawia, że ​​logika jest samodzielna. Następnie używam jednego pliku aplikacji, aby rozdzielić obowiązki na różne klasy, czyli około 3 wierszy kodu na trasę. W typowej aplikacji nie ma wielu tras, więc mój plik aplikacji nie jest tak długi.
Tom Andersen,
1
Czy powszechną praktyką jest definiowanie klasy w wielu plikach? W każdym pliku na nowo definiujesz „MyApp”. Jestem nowy w rubinach, więc wydaje mi się to dziwne. Jaki jest tego powód?
0xSina
5
@ 0xSina W Ruby nie jest niczym niezwykłym. Nie „definiujesz” klasy, „ponownie ją otwierasz”. Na przykład Arrayklasa jest definiowana przez bibliotekę podstawową, ale można później „monkeypatch” za pomocą class Array; def some_awesome_method; endi a) wszystkie poprzednie funkcje macierzy są zachowane, i b) wszystkie instancje macierzy otrzymają nowy kod. Klasy w Rubim są tylko przedmiotami i mogą być w każdej chwili ulepszane i zmieniane.
Phrogz
10

Absolutnie. Aby zobaczyć przykład tego, polecam pobranie klejnotu Mnicha, opisanego tutaj:

https://github.com/monkrb/monk

Możesz „zainstalować klejnot” za pośrednictwem rubygems.org. Gdy zdobędziesz klejnot, wygeneruj przykładową aplikację, korzystając z instrukcji podanych powyżej.

Zauważ, że nie musisz używać Monka do faktycznego rozwoju, chyba że chcesz (w rzeczywistości myślę, że może nie być aktualny). Chodzi o to, aby zobaczyć, jak możesz łatwo zbudować aplikację w stylu MVC (z osobnymi plikami tras przypominającymi kontroler), jeśli chcesz.

Jest to dość proste, jeśli spojrzysz na to, jak Monk sobie z tym radzi, głównie wymagając plików w osobnych katalogach, coś w stylu (musisz zdefiniować ścieżkę root):

Dir[root_path("app/**/*.rb")].each do |file|
    require file
end
TK-421
źródło
7
Jedną fajną rzeczą w stosowaniu jawnego init.rbkontra powyższego jest to, że możesz kontrolować kolejność ładowania, w przypadku, gdy masz współzależne pliki.
Phrogz
10

Wyszukaj w Google hasło „płyta kotłowa Sinatra”, aby uzyskać pomysły na to, jak inni układają swoje aplikacje Sinatra. Z tego prawdopodobnie możesz znaleźć taki, który odpowiada Twoim potrzebom lub po prostu stworzyć własny. Nie jest to zbyt trudne. W miarę opracowywania kolejnych aplikacji Sinatra możesz dodawać je do swojego szablonu.

Oto, co zrobiłem i wykorzystałem do wszystkich moich projektów:

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

Robert Ziehl
źródło
7

Wiem, że to stare zapytanie, ale wciąż nie mogę uwierzyć, że nikt nie wspomniał o Padrino. Możesz użyć go jako szkieletu na Sinatrze lub fragmentarycznie dodając tylko kamienie, które Cię interesują. Kopie dziesięć tyłków!

Steven Garcia
źródło
Zgadzam się, powinieneś rzucić okiem na Padrino, to kołysze!
NicoPaez
2

Moje podejście do hostingu różnych projektów w tej samej witrynie polega na takim użyciu 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
    ...

Innym szczegółem na temat podprojektów, których użyłem, było dodanie ich nazw, opisu i tras do jakiejś zmiennej globalnej, która jest używana "/"do stworzenia strony głównej przewodnika, ale nie mam teraz fragmentu kodu.

Nakilon
źródło
1

Czytanie dokumentów tutaj:

Rozszerzenia Sinatra

Wygląda na to, że Sinatra pozwala na dekompozycję aplikacji na moduły Ruby, które można pobrać za pomocą metody „register” Sinatra lub metod „helpers”, takich jak:

helpers.rb

require 'sinatra/base'

module Sinatra
  module Sample
    module Helpers

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

    end
  end
end

routing / 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
Erin Swenson-Healey
źródło
1

Kiedy Monk nie działał dla mnie, sam zacząłem pracować nad szablonami.

Jeśli się nad tym zastanowić, nie ma nic specjalnego w wiązaniu zestawu plików. Filozofia mnichów została mi wyjaśniona na początku 2011 roku podczas RedDotRubyConf i powiedzieli mi konkretnie, że korzystanie z niej jest naprawdę opcjonalne, zwłaszcza teraz, gdy nie jest ona utrzymywana.

To dobry początek dla tych, którzy chcą korzystać z ActiveRecord:

Prosta Sinatra MVC

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

kgpdeveloper
źródło
1

Kluczem do modułowości w Sinatrze w przypadku większych projektów jest nauka korzystania z podstawowych narzędzi.

SitePoint ma bardzo dobry samouczek, z którego można zobaczyć modułowe aplikacje Sinatra i pomocników. Należy jednak zwrócić szczególną uwagę na jeden ważny szczegół. Ciągle wiele aplikacji Sinatra i zamontować je Rackup. Kiedy wiesz, jak napisać podstawową aplikację, spójrz na plik config.ru tego samouczka i obserwuj, jak montują niezależne aplikacje Sinatra.

Gdy nauczysz się obsługiwać Sinatrę z Rackiem, otworzy się zupełnie nowy świat strategii modułowości. To oczywiście zachęca do wypróbowania czegoś naprawdę przydatnego: teraz możesz polegać na posiadaniu indywidualnych Klejnotów dla każdej pod-aplikacji , co może umożliwić łatwą wersję modułów.

Nie lekceważ mocy korzystania z modułów klejnotów w swojej aplikacji. Możesz łatwo przetestować zmiany eksperymentalne w dobrze ograniczonym środowisku i łatwo je wdrożyć. Równie łatwo cofnąć, jeśli coś pójdzie nie tak.

Istnieje tysiące sposobów organizacji kodu, więc nie zaszkodzi próba uzyskania układu podobnego do Railsów. Istnieje jednak kilka świetnych postów na temat dostosowywania własnej struktury. Ten post pokrywa inne częste potrzeby większości programistów.

Jeśli masz czas, zachęcam do zapoznania się z Rack, wspólnym gruntem dla dowolnej aplikacji internetowej opartej na Ruby. Może to mieć znacznie mniejszy wpływ na to, jak wykonujesz swoją pracę, ale zawsze są pewne zadania, które większość ludzi wykonuje w swoich aplikacjach, które lepiej pasują do oprogramowania pośredniego Rack.

SystematicFrank
źródło