การใช้ Sinatra สำหรับโครงการขนาดใหญ่ผ่านหลายไฟล์


184

ดูเหมือนว่าใน Sinatra ตัวจัดการเส้นทางทั้งหมดจะถูกเขียนเป็นไฟล์เดียวถ้าฉันเข้าใจถูกต้องมันจะทำหน้าที่เป็นตัวควบคุมขนาดใหญ่ / ขนาดเล็ก มีวิธีใดบ้างที่จะแบ่งออกเป็นไฟล์อิสระที่แยกต่างหากดังนั้นเมื่อมีคนเรียก "/" - ดำเนินการอย่างใดอย่างหนึ่งและหากได้รับ smth เช่น "/ posts / 2" จากนั้นดำเนินการอื่น - ตรรกะที่คล้ายกันซึ่งใช้ใน PHP ?

คำตอบ:


394

นี่คือเทมเพลตพื้นฐานสำหรับแอป Sinatra ที่ฉันใช้ (แอพที่ใหญ่กว่าของฉันมี 200 ไฟล์ที่แตกออกมาเช่นนี้ไม่นับผู้ขายอัญมณีจะครอบคลุม 75-100 เส้นทางที่ชัดเจนเส้นทางเหล่านี้บางเส้นทางเป็นเส้นทาง Regexp ที่ครอบคลุมรูปแบบเส้นทางเพิ่มเติม 50+) เมื่อใช้ Thin คุณจะเรียกใช้ แอพแบบนี้โดยใช้:
thin -R config.ru start

แก้ไข : ฉันตอนนี้การรักษาของตัวเองพระโครงกระดูกบนพื้นฐานดังต่อไปนี้เรียกว่า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
ผู้ช่วย /
  init.rb
  partials.rb
รุ่น /
  init.rb
  user.rb
เส้นทาง /
  init.rb
  login.rb
  main.rb
มุมมอง /
  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

11
สิ่งหนึ่งที่ดีเป็นพิเศษเกี่ยวกับโครงสร้างข้างต้นโดยเฉพาะการวางrequire "sequel"และการกำหนดDBค่าเริ่มต้นในmodels/init.rbและการใช้require_relativeสำหรับไฟล์ทั้งหมดคือคุณสามารถ cd ลงในmodelsไดเรกทอรีของคุณเปิดคอนโซล IRB และประเภทrequire './init'และคุณมีฐานข้อมูลและการตั้งค่าแบบจำลองทั้งหมดสำหรับการสำรวจเชิงโต้ตอบ .
Phrogz

1
โครงสร้างตัวอย่างที่ยอดเยี่ยมสมบูรณ์แบบสำหรับ noatra Sinatra เช่นฉันไชโย
Barry Jordan

27
ฉันใช้วิธีการอื่น รหัสตรรกะทางธุรกิจทั้งหมดเช่นผู้ใช้และบริการในทับทิมโดยไม่ต้องใช้ 'ซินาตร้า' สิ่งนี้ทำให้ตรรกะยืนได้ด้วยตัวของมันเอง จากนั้นฉันก็ใช้ไฟล์แอพเดียวเพื่อแจกความรับผิดชอบให้กับคลาสต่างๆดังนั้นจึงมีรหัสประมาณ 3 บรรทัดต่อเส้นทาง แอปพลิเคชั่นทั่วไปมีเส้นทางไม่มากดังนั้นไฟล์แอพของฉันก็ไม่ได้ยาวมาก
Tom Andersen

1
เป็นวิธีปฏิบัติทั่วไปในการกำหนดคลาสในหลายไฟล์หรือไม่? คุณกำลังกำหนด 'MyApp' ซ้ำแล้วซ้ำอีกในทุกไฟล์ ฉันใหม่กับทับทิมดังนั้นมันจึงแปลกสำหรับฉัน อะไรคือเหตุผลเบื้องหลังสิ่งนี้
0xSina

5
@ 0xSina ไม่ใช่เรื่องแปลกในรูบี คุณไม่ได้ "กำหนด" คลาสคุณ "เปิดใหม่อีกครั้ง" ตัวอย่างเช่นArrayคลาสจะถูกกำหนดโดยไลบรารีหลัก แต่คุณสามารถ "monkeypatch" ในภายหลังโดยใช้class Array; def some_awesome_method; endและ a) ฟังก์ชัน Array ก่อนหน้านี้ทั้งหมดได้รับการเก็บรักษาไว้และ b) อินสแตนซ์ Array ทั้งหมดจะได้รับรหัสใหม่ของคุณ คลาสใน Ruby เป็นเพียงวัตถุและอาจถูกเพิ่มและเปลี่ยนแปลงได้ตลอดเวลา
Phrogz

10

อย่างแน่นอน หากต้องการดูตัวอย่างนี้ฉันขอแนะนำให้ดาวน์โหลดพระ Monk ที่อธิบายไว้ที่นี่:

https://github.com/monkrb/monk

คุณสามารถ 'ติดตั้งอัญมณี' ได้ทาง rubygems.org เมื่อคุณมีอัญมณีแล้วให้สร้างแอปตัวอย่างโดยใช้คำแนะนำที่ลิงก์ไว้ด้านบน

โปรดทราบว่าคุณไม่จำเป็นต้องใช้พระสำหรับการพัฒนาที่แท้จริงของคุณเว้นแต่คุณต้องการ (อันที่จริงฉันคิดว่ามันอาจไม่เป็นปัจจุบัน) ประเด็นคือเพื่อดูว่าคุณสามารถจัดโครงสร้างแอปของคุณในรูปแบบ MVC ได้อย่างไร (พร้อมไฟล์เส้นทางเหมือนตัวควบคุมแยกต่างหาก) หากคุณต้องการ

มันค่อนข้างง่ายถ้าคุณดูว่าพระจัดการอย่างไรส่วนใหญ่เป็นเรื่องของการต้องการไฟล์ในไดเรกทอรีแยกกันเช่น (คุณจะต้องกำหนด root_path):

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

7
สิ่งหนึ่งที่ดีเกี่ยวกับการใช้อย่างชัดเจนinit.rbเมื่อเทียบกับด้านบนคือคุณสามารถควบคุมลำดับการโหลดได้ในกรณีที่คุณมีไฟล์ที่พึ่งพาซึ่งกันและกัน
Phrogz

10

ทำการค้นหาโดย Google สำหรับ "Sinatra boilerplate" เพื่อรับแนวคิดบางอย่างเกี่ยวกับวิธีที่คนอื่น ๆ กำลังจัดวางแอปพลิเคชัน Sinatra ของพวกเขา จากนั้นคุณอาจจะพบคนที่เหมาะกับความต้องการของคุณหรือทำด้วยตัวเอง ไม่ยากเกินไปที่จะทำ ในขณะที่คุณพัฒนาแอพ Sinatra มากขึ้นคุณสามารถเพิ่มไปยังส่วนสำเร็จรูป

นี่คือสิ่งที่ฉันทำและใช้สำหรับโครงการทั้งหมดของฉัน:

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


7

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันก็ยังไม่อยากเชื่อเลยว่าไม่มีใครพูดถึงPadrinoคุณสามารถใช้มันเป็นกรอบบนสุดของ Sinatra หรือเพิ่มจำนวนอัญมณีที่คุณสนใจเท่านั้น มันเตะก้นสิบตูด!


ฉันเห็นด้วยคุณควรดู Padrino มันสั่นคลอน!
NicoPaez

2

แนวทางของฉันในการโฮสต์โปรเจ็กต์ต่าง ๆ ในไซต์เดียวกันคือใช้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
    ...

รายละเอียดอื่น ๆ เกี่ยวกับโครงการย่อยที่ฉันใช้คือการเพิ่มชื่อคำอธิบายและเส้นทางไปยังตัวแปรทั่วโลกบางประเภทที่ใช้"/"เพื่อสร้างหน้าแรกของคำแนะนำ แต่ตอนนี้ฉันไม่มีตัวอย่างข้อมูล


1

อ่านเอกสารได้ที่นี่:

ส่วนขยายของซินาตร้า

ดูเหมือนว่า Sinatra ช่วยให้คุณสามารถแยกแอปพลิเคชันของคุณลงในโมดูลทับทิมซึ่งสามารถดึงผ่านวิธี "register" หรือ "ผู้ช่วย" ของ Sinatra เช่น:

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

1

เมื่อพระไม่ทำงานสำหรับฉันฉันเริ่มทำงานกับแม่แบบด้วยตัวเอง

หากคุณคิดเกี่ยวกับมันไม่มีอะไรพิเศษเกี่ยวกับการผูกชุดของไฟล์ ปรัชญาของพระได้รับการอธิบายให้ฉันทราบเมื่อต้นปี 2554 ในช่วง RedDotRubyConf และพวกเขาได้บอกฉันโดยเฉพาะว่าเป็นตัวเลือกที่จะใช้งานได้จริง

นี่เป็นการเริ่มต้นที่ดีสำหรับผู้ที่ต้องการใช้ ActiveRecord:

Simple Sinatra MVC

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


1

กุญแจสำคัญสำหรับความเป็นโมดุลของซินาตร้าสำหรับโครงการขนาดใหญ่คือการเรียนรู้การใช้เครื่องมือพื้นฐาน

SitePoint มีบทแนะนำที่ดีมากซึ่งคุณสามารถดูแอปและผู้ช่วยเหลือของ Sinatra ได้ อย่างไรก็ตามคุณควรใส่ใจเป็นพิเศษกับรายละเอียดที่สำคัญอย่างหนึ่ง คุณเก็บแอพ Sinatra ไว้หลายตัวและติดตั้งเข้ากับ Rackup เมื่อคุณรู้วิธีการเขียนแอพพื้นฐานดูที่config.ruไฟล์ของบทช่วยสอนนั้นและสังเกตว่าพวกเขาติดตั้งแอพ Sinatra อิสระอย่างไร

เมื่อคุณเรียนรู้การใช้งาน Sinatra พร้อม Rack โลกใหม่ของกลยุทธ์แบบแยกส่วนจะเปิดขึ้น เห็นได้ชัดว่าเชื้อเชิญให้ลองใช้สิ่งที่มีประโยชน์จริง ๆ : ตอนนี้คุณสามารถพึ่งพาการมีอัญมณีแยกต่างหากสำหรับแต่ละแอปพลิเคชันย่อยสิ่งที่อาจทำให้คุณสามารถรุ่นโมดูลของคุณได้อย่างง่ายดาย

อย่าประมาทพลังของการใช้โมดูลอัญมณีสำหรับแอปของคุณ คุณสามารถทดสอบการเปลี่ยนแปลงทดสอบในสภาพแวดล้อมที่มีตัวคั่นได้อย่างง่ายดายและปรับใช้อย่างง่ายดาย ง่ายต่อการย้อนกลับอย่างเท่าเทียมกันหากมีสิ่งผิดปกติ

มีวิธีการพันในการจัดระเบียบโค้ดของคุณดังนั้นมันจะไม่เจ็บที่จะลองใช้เลย์เอาต์ที่คล้ายกับ Rails อย่างไรก็ตามยังมีโพสต์ที่ยอดเยี่ยมเกี่ยวกับวิธีกำหนดโครงสร้างของคุณเอง โพสต์นั้นครอบคลุมถึงความต้องการที่พบบ่อยอื่น ๆ ของนักพัฒนาเว็บส่วนใหญ่

หากคุณมีเวลาฉันขอแนะนำให้คุณเรียนรู้เพิ่มเติมเกี่ยวกับ Rack ซึ่งเป็นจุดเริ่มต้นสำหรับเว็บแอพพลิเคชันที่ใช้ Ruby มันอาจมีผลกระทบน้อยกว่ามากในวิธีที่คุณทำงานของคุณ แต่มีงานบางอย่างที่คนส่วนใหญ่ทำในแอพของพวกเขาที่เหมาะกับการเป็นมิดเดิลแวร์แบบแร็ค

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.