सिनात्रा एक रूबी वेब ढांचा है।
यह रेल के छोटे भाई की तरह है…
आइए जानें कि सिनात्रा कैसे काम करती है :
- क्या होता है जब आपको अपने प्रोजेक्ट में सिनात्रा की आवश्यकता होती है?
- मार्ग मिलान कैसे कार्य करता है?
- अनुरोधों और प्रतिक्रियाओं को कैसे संसाधित किया जाता है?
इतने सारे सवाल, लेकिन इतना कम समय…
कोई बात नहीं!
मैंने आपके लिए कड़ी मेहनत की है और इस लेख को एक साथ रखा है जहां मैं इन सवालों के जवाब देता हूं ताकि आप तेजी से सीख सकें!
सिनात्रा इनिशियलाइज़ेशन
यह सब एक फ़ाइल से शुरू होता है:sinatra.rb ।
यह सब फ़ाइल करने के लिए main.rb की आवश्यकता होती है , बहुत रोमांचक नहीं है ना?
अब यह और दिलचस्प हो गया है!
अंदर main.rb आपको base.rb . के लिए एक आवश्यकता मिलेगी और आपको विकल्प पार्सिंग (पोर्ट, पर्यावरण, शांत मोड, आदि) के लिए कोड भी मिलेगा।
सिनात्रा optparse . का उपयोग करता है , रूबी के मानक पुस्तकालय से।
आपको यहाँ और क्या मिल सकता है?
इस पर एक नज़र डालें at_exit ब्लॉक करें:
at_exit { Application.run! if $!.nil? && Application.run? }
यह एक छोटा सा कोड है जो प्रोग्राम के समाप्त होने पर चलेगा।
क्या होता है कि आपका सारा कोड रूबी द्वारा पढ़ा जाएगा और चूंकि आपके पास कोई लूप, स्लीप या ऐसा कुछ नहीं है, इसलिए आपका प्रोग्राम स्वाभाविक रूप से समाप्त हो जाएगा।
...लेकिन इसके समाप्त होने से ठीक पहले at_exit ब्लॉक ट्रिगर होगा!
फिर सिनात्रा एक वेब सर्वर को संभालती है और शुरू करती है ताकि वह अनुरोधों को संभाल सके।
यहां वह कोड दिया गया है जो ऐसा करता है :
begin
start_server(handler, server_settings, handler_name, &block)
rescue Errno::EADDRINUSE
$stderr.puts "== Someone is already performing on port #{port}!"
raise
end
# Part of base.rb `run!` method
ओह और एक और महत्वपूर्ण बात यहाँ होती है:
extend Sinatra::Delegator
Sinatra::Delegator एक मॉड्यूल है जो सिनात्रा डीएसएल विधियों को परिभाषित करता है जैसे get , post &set ।
इसलिए आप ऐसा कर सकते हैं:
get '/' do puts "Hello World!" end
सिनात्रा वैश्विक main का विस्तार करता है इस मॉड्यूल के साथ ऑब्जेक्ट करें।
अनुरोध और प्रतिक्रिया प्रसंस्करण
ठीक है, तो इस समय हमारे पास एक रनिंग सर्वर है जो नए कनेक्शन स्वीकार करने के लिए तैयार है।
लेकिन क्या होता है जब एक नया कनेक्शन प्राप्त होता है?
खैर सिनात्रा, रेल और अन्य रूबी वेब ढांचे की तरह, सभी निचले स्तर की सामग्री को संभालने के लिए रैक का उपयोग करता है।
रैक एक call की अपेक्षा करता है आपके आवेदन पर उपलब्ध होने की विधि। जब आप रैक को इनिशियलाइज़ करते हैं तो यह केवल एक वस्तु है जिसे आप रैक को देते हैं।
सिनात्रा के मामले में यह वस्तु Sinatra::Base . है कक्षा।
यह रहा तरीका :
# Rack call interface.
def call!(env)
@env = env
@request = Request.new(env)
@response = Response.new
invoke { dispatch! }
invoke { error_block!(response.status) } unless @env['sinatra.error']
@response.finish
end
# Modified version of Sinatra's call method (for clarity)
ऐसा लगता है कि हमें dispatch! . की जांच करने की आवश्यकता है अनुरोध को कैसे हैंडल किया जाता है, यह दिखाने के लिए आगे की विधि।
यह रहा वह तरीका :
def dispatch!
invoke do
static! if settings.static? && (request.get? || request.head?)
filter! :before
route!
end
rescue ::Exception => boom
invoke { handle_exception!(boom) }
ensure
filter! :after unless env['sinatra.static_file']
end
# Edited down to the important parts
अनुरोध 4 चरणों में विभाजित है :
- स्टेटिक फाइलों को पहले चेक किया जाता है। ये सीएसएस, जेएस और इमेज जैसी फाइलें हैं। यदि "सार्वजनिक" नाम की निर्देशिका मौजूद है तो यह सेटिंग डिफ़ॉल्ट रूप से सक्षम होती है
- पहले वाला फ़िल्टर चलाया जाता है
- रूट मिलान
- आफ्टर फ़िल्टर चलाया जाता है
अब हम प्रत्येक चरण की गहराई से जांच कर सकते हैं कि क्या होता है और अधिक विस्तार से।
स्थिर फ़ाइलें प्रस्तुत करना
static! विधि बहुत आसान है:
def static!(options = {})
return if (public_dir = settings.public_folder).nil?
path = File.expand_path("#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" )
return unless File.file?(path)
cache_control(*settings.static_cache_control) if settings.static_cache_control?
send_file(path, options)
end
यह कोड जांचता है कि अनुरोधित फ़ाइल मौजूद है या नहीं, तो यह "कैश नियंत्रण" HTTP शीर्षलेख सेट करता है।
अंतिम पंक्ति में यह send_file . को कॉल करता है और यह वही करता है जो नाम कहता है 🙂
फ़िल्टर करने से पहले
एक पहले वाला फ़िल्टर आपको मेल खाने वाले मार्ग को खोजने का प्रयास करने से पहले कोड चलाने की अनुमति देता है।
इस प्रकार फ़िल्टर जोड़ा जाता है:
# Define a before filter.
# Runs before all requests within the same context as route handlers
# and may access/modify the request and response.
@filters = {:before => [], :after => []}
def before(path = /.*/, **options, &block)
add_filter(:before, path, options, &block)
end
def after(path = /.*/, **options, &block)
add_filter(:after, path, options, &block)
end
def add_filter(type, path = /.*/, **options, &block)
filters[type] << compile!(type, path, block, options)
end
जैसा कि आप filters देख सकते हैं केवल दो कुंजियों वाला एक हैश है, प्रत्येक फ़िल्टर प्रकार के लिए एक।
लेकिन compile! ?
यह विधि 3 तत्वों के साथ एक सरणी लौटाती है:एक पैटर्न, शर्तों की एक सरणी और एक आवरण।
मार्ग बनाने के लिए उसी विधि का उपयोग किया जाता है (जब आप get . का उपयोग करते हैं या post ब्लॉक):
def get(path, opts = {}, &block)
route('GET', path, opts, &block)
end
def route(verb, path, options = {}, &block)
signature = compile!(verb, path, block, options)
(@routes[verb] ||= []) << signature
signature
end
# Methods edited for clarity
इससे हम सीख सकते हैं कि सिनात्रा फ़िल्टर मार्गों की तरह ही व्यवहार और कार्य करते हैं।
रूट मिलान
अनुरोध संसाधन चक्र का अगला चरण मार्ग मिलान है:
def route!(base = settings, pass_block = nil)
routes = base.routes[@request.request_method]
routes.each do |pattern, conditions, block|
process_route(pattern, conditions)
route_eval
end
route_missing
end
# Edited method
यह कोड अनुरोध विधि से मेल खाने वाले प्रत्येक मार्ग पर जाता है (get , post , आदि)।
रूट मिलान process_route . के अंदर होता है विधि:
def process_route(pattern, keys, conditions, block = nil, values = []) route = @request.path_info route = '/' if route.empty? and not settings.empty_path_info? return unless match = pattern.match(route) end
जहां pattern एक नियमित अभिव्यक्ति है।
यदि कोई मार्ग पथ और शर्तों दोनों से मेल खाता है तो route_eval कॉल किया जाएगा, जो ब्लॉक का मूल्यांकन करता है (आपके get . का मुख्य भाग / post मार्ग) और मार्ग मिलान प्रक्रिया को समाप्त करता है।
# Run a route block and throw :halt with the result. def route_eval throw :halt, yield end
यह असामान्य catch . का उपयोग करता है / throw प्रवाह नियंत्रण के लिए तंत्र।
मैं इसके खिलाफ अनुशंसा करता हूं क्योंकि यह कोड के प्रवाह का पालन करने में बहुत भ्रमित हो सकता है, लेकिन उपयोग में इस सुविधा का वास्तविक दुनिया का उदाहरण देखना दिलचस्प है।
प्रतिक्रिया भवन
अनुरोध चक्र का अंतिम चरण प्रतिक्रिया तैयार करना है।
तो प्रतिक्रिया कहाँ जाती है?
invoke विधि इस तरह प्रतिक्रिया एकत्र करती है:
res = catch(:halt) { yield }
यह परिणाम body . का उपयोग करके प्रतिक्रिया निकाय को सौंपा गया है विधि:
body(res)
अब अगर हम पीछे मुड़कर देखें कि हमने कहां से शुरुआत की थी, तो call विधि, हम कोड की यह पंक्ति पाएंगे:
@response.finish
यह finish को कॉल करता है @response . पर विधि , जो एक Rack::Response . है वस्तु।
दूसरे शब्दों में, यह वास्तव में क्लाइंट को भेजी जाने वाली प्रतिक्रिया को ट्रिगर करेगा।
बोनस:सेट मेथड कैसे काम करता है
सेट विधि सिनात्रा की डीएसएल (डोमेन-विशिष्ट भाषा) का हिस्सा है और यह आपको अपने सिनात्रा एप्लिकेशन में कहीं भी कॉन्फ़िगरेशन विकल्प सेट करने देती है।
उदाहरण :
set :public_folder, '/var/www'
हर बार जब आप set . का उपयोग करते हैं सिनात्रा 3 तरीके बनाता है (मेटाप्रोग्रामिंग के माध्यम से):
define_singleton("#{option}=", setter) if setter
define_singleton(option, getter) if getter
define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
3 तरीके हैं (public_folder . के साथ) उदाहरण के तौर पर):
- public_folder
- public_folder=
- public_folder?
यह विधि सेटर विधि को भी बुलाएगी (public_folder= ) अगर यह पहले से मौजूद है:
if respond_to?("#{option}=") && !ignore_setter
return __send__("#{option}=", value)
end
याद रखें कि मेटाप्रोग्रामिंग मुफ़्त नहीं है, इसलिए मैं केवल options के साथ रहूंगा हैश। आपको उन फैंसी तरीकों की आवश्यकता नहीं है।
सारांश
आपने सीखा कि सिनात्रा कैसे आरंभ होता है, यह कैसे एक अनुरोध को संभालता है और प्रतिक्रिया उत्पन्न होने तक विभिन्न कदम उठाता है। इससे आपको कुछ रूबी ट्रिक्स सीखने और सिनात्रा को बेहतर ढंग से समझने में मदद मिलेगी!
इस पोस्ट को शेयर करना न भूलें अन्य रूबी डेवलपर्स के साथ ताकि वे भी इससे सीख सकें 🙂