שירים מתקדמים עם סינטרה חלק ראשון

יש לי פרוייקט ארוך טווח, שדורש ממני ליצור גם API מבוסס web, גם ממשק גרפי שידבר איתו, ועוד מספר דברים שונים.

כאשר התחלתי לחקור מה אני צריך לנושא, התחלתי לעשות בדיקות של הרבה מאוד סביבות עבודה, בניהם django, rails, ramaze, dancer, padrino, tornado ועוד מספר מערכות מבוססות Node.js.

הגעתי להבנה, כי אף אחת מהן לא גמישה בשבילי, היות והדרישות משתנות, התפיסה משתנה אחת למספר חודשים, וצריך לנוע מהר מאוד בשינוי התפיסה, כולל שינוי בכלל מהחשיבה המקורית. כלומר הכלים טובים, אבל ברגע שאתה מתחיל לעבוד בגישה שלהם, אתה מקבע את עצמך בצורה כזו או אחרת, וקשה מאוד לצאת משם (ובrails מראש אתה נעול ל"דעה" של מפתחי הסביבה).

אז למרות שמאוד פחדתי, הלכתי ובדקתי את Sinatra (שוב). חשוב לי להדגיש כי זהו framework מדהים אשר אני מאוד אוהב אותו, אבל הוא נראה לי מנימליסטי מידי לצרכים שלי.

עם התחלה מאוד צולעת, וכמעט הרצון לנטוש אותו ובכל זאת ללכת למפלצות הגדולות כדוגמת rails ו django, החלטתי לשנות כיוון וחשיבה. החלטתי לחשוב איך לגרום לסינטרה לעבוד עבורי. אז התחלתי להיכנס ממש לעובי הקורה, ושבוע שלם רק למדתי לחשוב בסינטרה.למרבה השמחה, התוצאות לא איחרו לבוא, וכרגע הפרוייקט שנמצא באמצע השלב הראשוני שלו, נראה כאילו היה פרוייקט של ramaze או django מבחינת מבנה ספריות וקבצים, אבל הוא Sinatra נקי.

אז כיצד גורמים לSinatra להיות יותר ממה שהוא נראה בהתחלה ? ובכן דבר ראשון מבינים איך רובי עובדת !

רובי לטוב ולרע עובדת כמו C מבחינת עבודה עם קבצים. מה הכוונה ? ה require בעצם טוען למרחב שלנו את התוכן של הקובץ, כך שיש לנו בסופו של דבר רק קובץ אחד "אמיתי", וכל השאר הם הדרך שלנו לפרק דברים לגורמים. במרבית המקרים של frameworks מבוססי רובי, יקראו לקובץ config.ru .בניגוד למה שהרבה חושבים, זהו קובץ שבעצם נועד לעבודה עם rack והוא בעצם הבסיס של סביבות מבוססות rack.

הקובץ הוא בעצם הקובץ הראשי שלנו ! הוא בברירת המחדל יראה משהו בסגנון הזה:

#!/usr/bin/env ruby

require 'rubygems'
require 'sinatra'
require 'myapp'

set :environment, :development

run MyApp

כמובן שיהיו לו שינויים בעתיד. למשל אני מעדיף (וממליץ גם לכם) לעבוד עם Bundler, ויותר מאוחר נראה איך זה עובד איתו.

היות והאפליקציה שלנו נמצאת בקובץ בשם myapp.rb (כמובן שאפשר לשנות את זה, גמישות זו מילה ממש לא זרה כאן), ניצור אותו עם התוכן הבא:

class MyApp < Sinatra::Base
  get '/' do
    'hello world'
  end
end

מי שכבר עבד בצורה "רגילה" עם סינטרה, יעצור ושאל כאן מה עשיתי, היות ובד"כ זה אמור להראות אחרת.
במידה ונרצה בסינטרה, להיות מודולרים, כלומר להשתמש ביותר מקובץ אחד שיהיה ה controller שלנו (או איך שלא תקראו לזה), אנחנו צריכים להפריד רשויות. ההפרדה הזו גם מאפשרת לנו לעשות שימוש חוזר בקוד במספר פרוייקטים בלי להתנגש בכלום.

אבל עכשיו אנחנו צריכים להבין כי יש מספר דברים ש passenger דורש. למשל ספרייה בשם tmp בספרייה הראשית של אפליקציה שלנו, וכן ספרייה שהיא public. הספרייה של tmp היא בנוסף למשהו "זמני", גם סוג של ipc אל מול המודול. שם אפשר להגיד לו למשל לקרוא מחדש את האפליקציה שלנו, וכו' …

לאחר יצירת הספריות, ניתן לעבוד עם Passenger. אבל אני אוהב להשתמש בחד קרן, אומנם הצבע הורוד לא משהו (לדעתי), אבל עדיין, הוא עובד טוב, ומאפשר לי להבין מה המשתמשים עושים, ובנוסף הוא יודע לעשות scale ולעבוד מול אפצ'י או nginx בגישה של Proxy.
מתקינים אותו גם כן באמצעות מנהל החבילות של רובי (מומלץ). אני אישית גם עובד עם rbenv, אבל אתם יכולים להשתמש ב rvm או כל שיטה אחרת שטובה לכם.
הרצת השרת נעשת בצורה הבאה:

$ unicorn -p 3000

זה אומר לו לרוץ בפורט 3000 במקום בפורט ברירת המחדל שלו. העניין הוא שכרגע השרת לא ידע לזהות כאשר שינינו קוד, ונהיה צריכים לאתחל אותו מחדש בכל שינוי של קוד. אז אני מצאתי כי Sinatra::Contrib היא הספרייה הכי חשובה שתוכלו להתקין בעבודה עם סינטרה, והיא מכילה מודול עבור טעינה מחדש של מחלקות או קוד בכלל. אתם רק צריכים לרשום את המחלקה שלכם לטעינה מחודשת.

היות ואנחנו רוצים לעבוד בסגנון של פריימוורק גדול, אני חשבתי כי לספק הגדרות בשורת הריצה עבור unicorn אינו רעיון חכם במיוחד, היות והשורה יכולה להיות ארוכה. אז יצרתי קובץ הגדרות שאני משתמש בו במקום:

# Unicorn configuration

worker_processes 1 # development ...
#worker_processes 5 # production ...

APP_PATH = "#{File.expand_path(File.dirname(__FILE__))}/"
working_directory APP_PATH

pid "#{APP_PATH}/tmp/unicorn.pid"

listen 3000

stderr_path "#{APP_PATH}/log/unicorn.stderr.log"
stdout_path "#{APP_PATH}/log/unicorn.stdout.log"

before_fork do |server, worker|
  old_pid = "#{APP_PATH}/tmp/unicorn.pid.oldbin"

  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

אנחנו נריץ את unicorn בצורה הבאה:

$ unicorn -c unicorn.rb

היות ואנחנו מתחילים לצבור תלויות, רצוי כאמור לעבוד עם bundler.
עבורו צריך ליצור קובץ בשם Gemfile:

source 'https://rubygems.org'

gem 'sinatra'         # the framework
gem 'sinatra-contrib' # adding Sinatra::Contrib

group :development do # for development use unicorn ...
  gem 'unicorn'
end

עכשיו לקובץ ה rack אצור את הפעולה הבאה:

require 'rubygems'
require 'bundler'

Bundler.require # handle requirements 
set :environment, :development

require 'sinatra/reloader' if development?  

# let's save the path as global variable, we will use it further ...
$app_path = "#{File.expand_path(File.dirname(__FILE__))}/" unless global_variables.include?('app_path')

require "$app_path/myapp" # explain unicorn how to locate your application

run MyApp

החלק הזה נראה מעט מורכב יותר, אבל למעשה הוא מעביר את ניהול התלויות ל Bundler.
בנוסף, צריך להגיד לunicorn איך למצוא את האפליקציה שלנו, ולכן אני הגדרתי משתנה גלובלי אשר מקבל את המיקום הנוכחי, ודרכו טוענים את האפליקציה.

בחלק הבא, נתחיל להרכיב את המבנה קצת קרוב יותר ל rails, אך אתם כאמור, יכולים לבחור בעצמכם כיצד לעשות זאת.

6 מחשבות על “שירים מתקדמים עם סינטרה חלק ראשון

  1. יוסק'ה

    מסתבר שאני לא כל כך טמבל, תמיד חשבתי שזה משהו אצלי, שאני מרגיש שכל סביבת פיתוח רוצה שאחשוב שלגלגל יש בכלל צורה אחרת ולא עגולה.
    לגלות שהפרנויה שלך מוצדקת יכול לפעמים לנחם.

    1. ik_5 מאת

      לא קשור לפרנויות, תלוי בשימוש שלך במערכת שנכתבת.
      אני ירדתי למשל מramaze כי הוא חצי אפוי, ומגביל בסוג הטמפלייטים. למשל אם אני בוחר בטמפלייט של erubies, אני לא יכול לבחור ב scss כטמפלייט נוסף (עבור css שבכלל מקומפל).

  2. פינגבק: Brook Framework | לראות שונה

  3. פינגבק: סינטרה מודולרית | לראות שונה

כתיבת תגובה

הזינו את פרטיכם בטופס, או לחצו על אחד מהאייקונים כדי להשתמש בחשבון קיים:

הלוגו של WordPress.com

אתה מגיב באמצעות חשבון WordPress.com שלך. לצאת מהמערכת / לשנות )

תמונת Twitter

אתה מגיב באמצעות חשבון Twitter שלך. לצאת מהמערכת / לשנות )

תמונת Facebook

אתה מגיב באמצעות חשבון Facebook שלך. לצאת מהמערכת / לשנות )

תמונת גוגל פלוס

אתה מגיב באמצעות חשבון Google+ שלך. לצאת מהמערכת / לשנות )

מתחבר ל-%s