آموزش NodeJS قسمت دوم

آموزش NodeJS قسمت دوم

معرفی لایه های اساسی برنامه

اگر قصد ساخت یک خانه داشته باشیم، احتمالا باید از یک پایه بسیار قوی شروع کنیم. ما نمیتوانیم تا وقتی که پایه ساختمان خود را به خوبی تقویت نکرده ایم تصمیم به ساخت چندین طبقه بر روی آن کنیم.

با این حال در نرم افزار کمی متفاوت است. ما می توانیم بدون داشتن یک پایه خوب شروع به توسعه برنامه کنیم. ما به این روش brute-force-driven development گوییم. اجازه دهید درباره پروژه فکر کنیم شبکه اجتماعی که قرار است با Node.js درست کنیم، با استفاده

از کد ساده زیر شروع می‌ کنیم:

var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(3000, '127.0.0.1'); console.log('Server running at http://localhost:3000');

اولین چیزی که در بالا توجه می کنید، نمایش دادن یک پیام به کاربر است، اما شاید بخواهید که محتوای یک فایل را نمایش دهید. Node.js بسیار شبیه به PHP می باشد، اما با این وجود یک تفاوت اساسی وجود دارد. PHP نیاز به یک سرور دارد تا درخواست ها (request) راجمع‌آوری کند و آنها را به مفسر PHP منتقل کند و پس از آن کد PHP پردازش شده و پاسخ (response) را دوباره از طریق سرور به کاربر ارسال کند. اما در دنیای Node.js ، نیازی به یک سرور خارجی (external) جداگانه نداریم. Node.js خوداین کار را برای ما انجام می‌دهد.این به توسعه دهندگان این امکان را می دهد تا به درخواست های آمده رسیدگی کنند و تصمیم بگیرند که به آنها چه کاری انجام دهند.

اگر به کد قبلی مراجعه کنیم و فرض کنیم که فایلی به نام page.html داریم که شامل یک HTML ساده و یک فایل styles.css که CSS ها را نگهداری می کند، قدم بعدی بصورت زیر خواهد بود:

var http = require('http'); var fs = require('fs'); http.createServer(function (req, res) { var content = ''; var type = ''; if (req.url === '/') { content = fs.readFileSync('./page.html'); type = 'text/html'; } else if (req.url === '/styles.css') { content = fs.readFileSync('./styles.css'); type = 'text/css'; } res.writeHead(200, {'Content-Type': type}); res.end(content + '\n'); }).listen(3000, '127.0.0.1'); console.log('Server running at http://localhost:3000');

در کد فوق ما درخواست های آمده URL را بررسی می کنیم. اگر آدرس /http://localhost:3000 یا http://127.0.0.1:3000 را با مرورگر باز کنید، کدهای page.html به عنوان پاسخ (response) به ما ارسال خواهد شد. اگر تگ link در فایل page.html داشته باشیم که styles.css را لینک می کند، مرورگر یک درخواست دیگر نیز برای آن ایجاد می‌کند. URL ها متفاوت هستند، اما با استفاده از بلوک if آنها را تشخیص می دهیم ومحتوای مناسب را نمایش می‌دهیم.

تا اینجا بسیار خوب پیش رفته ایم، اما باید این احتمال را در نظر بگیریم که نه تنها به دو فایل، بلکه به چندین فایل نیاز داریم. ما قرار نیست تمام آنها را توصیف کنیم. بنابراین، این پروسه باید بهینه سازی شود. اولین لایه از هر سرور Node.js معمولا با مسیرها ( route ) سر و کار دارد. این کار باعث می شود درخواست های URL را تجزیه و آنگاه تصمیم گیرد چه وظیفه ای را انجام دهد.اگر ما نیاز به ارائه فایل‌های استاتیک داشته باشیم، پس ما باید منطق هر بخش از برنامه را در یک ماژول خارجی جداگانه قرار دهیم تا عملیات پیدا کردن فایل ها، نوشتن آنها، و ارسال پاسخ با محتوای مناسب انجام دهد. این می‌تواند لایه دوم از معماری برنامه ما باشد.

همچنین همراه با ارسال فایل‌ها، نیاز به ا رسال برخی از منطق backend می باشد. این می‌تواند لایه سوم باشد. مجددا، این به URL بستگی دارد، ما برخی از اقدامات مربوط به منطق کار را انجام می‌دهیم، به عنوان مثال:

var http = require('http'); var fs = require('fs'); http.createServer(function (req, res) { var content = ''; var type = ''; if(req.url === '/') { content = fs.readFileSync('./page.html'); type = 'text/html'; } else if(req.url === '/styles.css') { content = fs.readFileSync('./styles.css'); type = 'text/css'; } else if(req.url === '/api/user/new') { // Do actions like // reading POST parameters // storing the user into the database content = '{"success": true}'; type = 'application/json'; } res.writeHead(200, {'Content-Type': type}); res.end(content + '\n'); }).listen(3000, '127.0.0.1'); console.log('Server running at http://127.0.0.1:3000/');

همانطور که ملاحضه می کنید ما داده های JSON را برگشت داده ایم. بنابراین، سرور Node.js ما به عنوان یک API رفتار می کند. ما با این موضوع در انتهای فصل بیشتر آشنا خواهیم شد.

اینها قرار است لایه های برنامه ما را تشکیل دهند. در فصل هایی که پیش رو داریم با آنها کار خواهیم کرد. اما قبل از آن اجازه دهید ببینیم چه کارهایی را قبل از رسیدن به آن نقطه باید انجام دهیم.

اجرای وظایف و ساخت سیستم

درکنار اجرا سرور Node.js ، شیوه های مختلف مربوط به توسعه وب وجود دارد که باید آنها را نیز در نظر گرفت. ما قرار است یک اپلیکیشن وب درست کنیم، بنابراین، ما از جاوا اسکریپت client-side و CSS که بهترین شیوه ممکن است استفاده می کنیم. به عبارت دیگر، برای افزایش عملکرد وب سایت ما،نیاز است که تمام فایل‌های JavaScript در داخل تنها یک فایل ادغام (merge) کنیم و آن را فشرده ( compress) کنیم. همچنین مجاز است که از همین روش برای فایل CSS استفاده کنیم.

اگر این کار را انجام دهید، باعث می‌ شود مرورگر درخواست کمتری به سرور ارسال می‌کند. Node.js یک ابزار متداول برای انجام خدمات خط فرمان است، به جز زمانی که می‌خواهید وب سرور وب اجرا کنید. ماژول های بسیاری در دسترس هستند که برای بسته بندی و بهینه سازی اجزای برنامه به کار می روند. این بسیار مهم است که task runner و build system‌هایی وجود دارند که به ما کمک می‌کنند تا این پروسه ها را مدیریت کنیم

معرفی Grunt

گرانت یکی از محبوبترین task runner هایی است که بر اساس Node.js ساخته شده است. گرانت در رجیستری پکیج منیجر (NPM) در دسترس است و می توانید به سادگی با دستور زیر آن را نصب کنید:

npm install –g grunt-cli

هنگامی که این دستور را در ترمینال خود اجرا کردیم، حال با دستور grunt می‌ توانیم از هر کجای سیستم خود به این ابزار دسترسی داشته باشیم. این به خاطر این است که گرانت بصورت global در سیستم نصب شده است.

برای شروع نیاز داریم تا فایل جدیدی به نام Gruntfile.jsدر ریشه پروژه خود درست کنیم(concatenation) در ریشه پروژه خود درست کنیم، در این فایل ما وظایفی (task) که می خواهیم بصورت خودکار انجام شوند را تعریف می کنیم. با تعریف این task ها می توانیم وظایفی مثل الحاق و کوچک سازی (minification) یا بهینه‌سازی را بر روی فایل های مشخص انجام دهیم. در زیر یک مثال ساده از فایل Gruntfile.js را مشاهده می کنید:

module.exports = function(grunt) { grunt.initConfig({ concat: { javascript: { src: 'src/**/*.js', dest: 'build/scripts.js' } } }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.registerTask('default', ['concat']); }

در آموزش قبل یاد گرفتیم که چطور می توانیم ماژول هایی را برای Node.js ایجاد کنیم. تنظیمات مورد نیاز برای Grunt نیز مثل یک ماژول ساده است. ما یک تابع را export‌ می‌کنیم که شی grunt شامل تمام توابع API عمومی می‌باشد. در بلوک initConfig اکشن‌های خود را قرار می‌دهیم و با registerTask اکشن‌ها و task‌ها را ترکیب می کنیم. با این وجود حداقل یک task می‌تواند همراه با نام default تعریف شود. این وقتی است که می‌خواهیمGrunt را بدون هیچ پارامتری در ترمینال اجرا کنیم.

آخرین تابعی که باید در مثال قبل به آن توجه شود loadNpmTasks است. قدرت واقعی Gruntوقتی نمایان می‌شود که ما می‌خواهیم از صدها پلاگین موجود استفاده کنیم. با استفاده از دستور grunt می‌توانید برای مدیریت این پلاگین‌ها استفاده کنید. از آنجایی که تمام این پلاگین‌ها در پکیج منیجر Node.js ذخیره شده‌اند، نیاز است که آنها را در فایل package.json قرار دهیم (بافایل package.json در فصل اول آشنا شدیم). به عنوان مثال برای کد قبل، دستورات زیر را دنبال کنید:

فایل package.json

{ "name": "gruntjs-test", "version": "0.0.1", "description": "GruntjsTest", "dependencies": {}, "devDependencies": { "grunt-contrib-concat": "^0.5.1" } }

اجازه دهید کار را با اضافه کردن دو ویژگی به Grunt ادامه دهیم. وقتی ما فایل های جاوا اسکریپت را concat کردیم، شاید بخواهیم یک نسخه فشرده شده (minify) از آن فایل را نیز داشته باشیم. grunt-contrib-uglify ماژولی است که این کار را برای ما انجام می دهد:

module.exports = function(grunt) { grunt.initConfig({ concat: { javascript: { src: 'src/**/*.js', dest: 'build/scripts.js' }, uglify: { javascript: { files: { 'build/scripts.min.js': '<%= concat.javascript.dest %>' } } } } }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('default', ['concat', 'uglify']); }

این را توجه داشته باشید که تسک uglify باید بعد از تسک concat اجرا شود چون آنها به یکدیگر وابسته هستند. همچنین یک میانبر وجود دارد <%= concat.javascript.dest %> که با استفاده از این عبارت می تونیم به راحتی فایل Gruntfile.js را قابل نگهداری کنیم.

تا اینجا Grunt برخی از وظایف را برای ما انجام می دهد، اما با این حال بسیار رنجش آور خواهد بود که به کنسول خود برگردیم و هر بار برای کوچکترین تغییر در پروژه دستور grunt را اجرا کنیم. اینجاست که باید از ماژول grunt-contrib-watch استفاده کنیم. این ماژولی است که فایل ها را زیر نظر می گیرد و وقتی تغییراتی در آنها مشاهده کند این تسک بطور خودکار اجرا خواهد شد. بطور ساده تر، وقتی بخشی از فایل پروژه خود را تغییر می دهید این تسک آن فایل را شناسایی می کند و بطور خودکار دستور grunt را برای ما اجرا می کند، تا لازم نباشد خود بطور دستی دستور grunt را در کنسول تایپ کنیم. فایل Gruntfile.js را بصورت زیر ویرایش کنید:

module.exports = function(grunt) { grunt.initConfig({ concat: { javascript: { src: 'src/**/*.js', dest: 'build/scripts.js' } }, uglify: { javascript: { files: { 'build/scripts.min.js': '<%= concat.javascript.dest %>' } } }, watch: { javascript: { files: ['<%= concat.javascript.src %>'], tasks: ['concat', 'uglify'] } } }); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('default', ['concat', 'uglify', 'watch']); }

حال برای اینکه اسکریپت را اجرا کنیم، باید دستور npm install grunt-contrib-watch grunt-contrib-uglify –-save-dev را در کنسول اجرا کنید تا ماژول ها نصب شوند، همانطور که

می‌دانیم این ماژول ها در دایرکتوری node_modules ذخیره می شوند. این دستور ماژول ها را نصب می کند و فایل package.json را آپدیت می کند. تصویر زیر نشان می دهد که چطور خروجی در ترمینال نمایش داده خواهد شد وقتی که دستور grunt اجرا شود:

Lucy@LUCY-PC /c/Users/lucy/Desktop/gruntjs-test/src $grunt Running "concat:javascript"(concat) task File build/scripts.js created. Running "uglify:javascript"(uglify)task >> 1 file created. Running ""watch" task Waiting … >>file"src\main.js"changed Running "concat:javascript"(concat)task File build/script.js created. Running :uglify:javascript" (uglify) task >>1 file created. Done, without errors Completed in 1.413s at tue sep 08 215 19:33:45 GMT+0430 (iran daylight time)- Waiting …. >>file "src\test.js" changed. File build/scripts.js created Running "uglify:javascript"(uglify) task >>1 file created. Done, without errors Completed in 1.393s at tue sep 08 2015 19:34:00 GMT+0430(iran daylight time)- Waiting

در اینجا مشاهده می کنید که چطور تسک های ما اجرا شدند و تسک watch نیز به درستی در حال اجرا می باشد. برای تست می توانید فایلی را تغییر داده تا بلافاصله تغییرات را در کنسول مشاهده کنید که چطور تسک ها بطور خودکار فایل را کامپایل می کنند. شما می‌ توانید بصورت من عمل کنید: ابتدا باید اشاره کنم که نویسنده از نسخه 0.3.0 برای ماژول grunt-contrib-concat استفاده کرده است، اما من از آخرین نسخه ماژول ها استفاده کرده ام، پس برای شما نیز نباید مشکلی پیش آید، اگر به هر دلیلی قادر به کامپایل برنامه نبودید یا با خطا روبرو می‌شدید، آنگاه می توانید از نسخه 0.3.0 برای ماژول concat استفاده کنید. تنها کافیست برای پیدا کردن نسخه مورد نظر به سایت رجیستری این ماژول ها مراجعه کنید تا هر نسخه ای که دوست دارید را انتخاب و شماره نسخه را در فایل package.json قرار داده و دوباره دستور npm install را اجرا کرده تا ماژول ها از نو دانلود و نصب شوند. به هر حال! برگردیم به پروژه، برای اینکه مطمئن شوید تسک ها به درستی کار می کنند یا نه، پوشه ای را در پروژه خود به نام src ایجاد کرده و درون آن دو فایل دلخواه جاوا اسکریپت ایجاد کنید

(main.js – test.js) و درون هر فایل با کدهای معتبر جاوا اسکریپت مقدار دهی کنید به عنوان مثال می توانید از کد ساده جاوا اسکریپت console.log(‘Hello World’); استفاده کنید. فایل ها را ذخیره کرده تا در کنسول مشاهده کنید چطور فایل های javascript کامپایل شده و در پوشه build ذخیره می شود.

معرفی Gulp

گالپ یک سیستم ساخت است که تسک ها را اتوماسیون می کند. درست مثل Grunt می‌ توانیم اجزای پروژه خود را مدیریت کنیم. با این حال، چند تفاوت بین این دو وجود دارد:

هنوز هم فایل پیکربندی داریم، اما gulpfile.js نامیده می شود.

گالپ یک ابزار مبتنی ب رجریان است. وقتی در حال اجرا است چیزی را در دیسک ذخیره نمی کند. Grunt نیاز به ایجاد یک فایل موقت به منظور انتقال داده ها از یک تسک به تسک دیگر است، اما گالپ داده ها را در حافظه نگه می دارد.

در فایل gulpfile.js ما تسک های خود را مثل اسکریپت ها عادی و منظم Node.jsمی نویسیم. ما آنها را در ادامه خواهیم دید.

برای استفاده از Gulp ، نیاز است که ابتدا آن را نصب کنیم. دستور زیر آن را بصورت global درسیستم نصب می کند:

npm install –g gulp

حال می توانید یک دایرکتوری جدید ایجاد کرده یا با همان دایرکتوری که برای Grunt کار می کردیم ادامه دهیم. با دستور npm install gulp گالپ را نصب کنید

در این بخش می خواهیم با افزونه های gulp-concat ، gulp-uglify و gulp-rename کار کنیم.

بعد از اینکه این ماژول ها را به فایل package.json اضافه کردید، دستور npm install را اجرا کنید تا آنها نصب شوند یا می توانید آنها را از طریق خط فرمان نصب کنید

مرحله بعد ایجاد فایل gulpfile.js در ریشه پروژه و اجرا دستور gulp در ترمینال است. بیایید کارهای مشابه ای که در بخش قبل انجام دادیم در Gulp نیز عملی کنیم:

var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var rename = require('gulp-rename'); gulp.task('js', function() { gulp.src('./src/**/*.js') .pipe(concat('scripts.js')) .pipe(gulp.dest('./build/')) .pipe(rename({suffix: '.min'})) .pipe(uglify()) .pipe(gulp.dest('./build/')) }); gulp.task('watchers', function() { gulp.watch('src/**/*.js', ['js']); }); gulp.task('default', ['js', 'watchers']);

در اسکریپ فوق ما ماژول هایی که نصب کرده بودیم را با استفاده از تابع require به برنامه اضافه کردیم، در واقع آنها را فراخوانی کردیم. با استفاده از شی عمومی gulp پلاگین های مورد نظر برای انجام کارها را تعریف کردیم. ما نیاز داریم که تمام این ماژول ها به فایل package.json اضافه کنیم. بعد از این کار، ما سه تسک با استفاده از سینتکس زیر تعریف می کنیم:

(task_name, callback_functio)

js : این تسکی است که فایل های جاوا اسکریپت ما دریافت می کند تا آنها را از طریق سیستم لوله کشی مخصوص به Gulp به افزونه cancat انتقال دهد و نتیجه را ذخیره کند. ما همینطور داده ها را با ارسال به ماژول uglify که کدهای ما را minify می‌کند کار را ادامه می‌دهیم و در آخر آنها را در یک فایل جدید با پس پسوند .min ذخیره می کنیم.

watchers : با استفاده از این تسک، می توانیم تغییرات در فایل ها را نظا رت کنیم و تسک js را اجرا کنیم.

default : بطور پیشفرض، Gulp آن بخشی از فایل را که ما با اضافه کردن چند آرگومان برای اجرا gulp قرار داده ایم را اجرا می کند.

نتیجه اسکریپت قبل شبیه به زیر است.

مجددا می توانیم مشاهده کنیم که خودکار سازی صورت گرفته است و watching نیز نمایش داده می شود.

$ gulp [22:13:22] Using gulpfile c\Users\Lucy\Desktop\gruntjs-test\gulpfile.js [22:13:22] starting 'js'… [22:13:22] finished 'js' after 18 ms [22:13:22] 'watchers'… [22:13:22] 'watchers' after 15 ms [22:13:22] 'default' … [22:13:22] ] 'default' after 6_27

توسعه آزمون محور (Test-driven development)

توسعه آزمون محور فرآیندی برای توسعه نرم افزار است که چرخه تست یک محصول جدید یا ویژگی جدید را اتوماسیون می کند. این کار سرعت توسعه در دراز مدت را افزایش و تمایل به تولید کد بهتر را افزایش می دهد. امروزه، بسیاری از فریم ورک ها دارای ابزاری هستند که به شما کمک می کنند تست خودکار ایجاد کنید. بنابراین به عنوان توسعه دهنده، ما نیاز داریم قبل از اینکه کد جدیدی را به برنامه اضافه کنیم ابتدا باید آن را بنویسیم و تست کنیم. ما همیشه نتیجه کار خود را بررسی می کنیم! در توسعه وب، ما معمولا مرورگر را باز می‌کنیم و بابرنامه تعامل می کنیم تا رفتار کدهای خود را مشاهده کنیم. پس، بخش عمده ای از زمان ما برای تست صرف می شود. خبر خوب این است که می‌توانیم این پروسه را بهینه کنیم. این باعث می شود کدهایی را بنویسیم که به جای ما آنها را انجام دهد. گاهی اوقات، متکی به تست دستی بهترین راه حل نیست، دلیل آن هم تنها یک چیز است، زمان! در زیر برخی از مزیت ها مهم داشتن تست را مشاهده می کنید:

تست باعث می شود تا برنامه ما قابل نگهداری و ثبات باشد

تست خودکار موجب صرفه جویی در زمان می شود که می تواند در بهبود کد سیستم موثر باشد

توسعه آزمون محور در طول زمان باعث می شود کد بهتری را ایجاد کنیم چون باعث می شود ما در مورد ساختار بهتر و روش های ماژولار فکر کنیم

تست مداوم کمک می کند تا ویژگی جدید را در برنامه موجود اضافه کنیم

تست می تواند به عنوان مستندات استفاده شود

در آغاز این روند، ما می خواهیم تست ما با شکست مواجعه شود. بعد از آن، گام به گام منطق مورد نظر برای عبور از این آزمون را پیاده سازی می کنیم.

در اغلب موارد، توسعه دهندگان از ابزارهایی استفاده می کنند تا به آنها در تست ها کمک می کند. ما می خواهیم از یک فریم ورک به نام Mocha استفاده کنیم. این ابزار هم برای Node.js و هم مرورگر قابل دسترس است. وقتی که حرف از خودکار سازی آزمون به میان می‌آیداین ابزار یکی از محبوبترین راه حل ها می باشند. اجازه دهید Mocha را نصب کنیم تا ببینیم TDD چطور کار می کند. همانطور که می بینید نصب ماژول ها بسیار آسان است پس Mocha را نیز بصورت زیرنصب کنید:

npm install –g mocha

همانطور که قبلا چندین بار در این کتاب انجام داده ایم، این بسته را هم بصورت global نصب می کنیم. با هدف یک مثال، فرض می کنیم که برنامه ما نیاز به ماژولی دارد که فایل های خارجی JSON را می خواند. بیایید یک پوشه خالی ایجاد کنیم و کدهای زیر را به فایل test.js اضافه کنیم:

var assert = require('assert'); describe('Testing JSON reader', function() { it('should get json', function(done) { var reader = require('./JSONReader'); assert.equal(typeof reader, 'object'); assert.equal(typeof reader.read, 'function'); done(); }); });

توابع describe و it توابع مخصوص به Mocha هستند. آنها سراسری (global) هستند و به آسانی می توانیم به آنها دسترسی داشته باشیم. ماژول assert یک ماژول توکار در Node.js است، پس لازم نیست آن را جداگانه دانلود و نصب کنید. با استفاده از این ماژول می توانیم عملیات بررسی را انجام دهیم. برخی از فریم ورک های محبوب برای تست متدهای خود را دارند.اما در Mocha اینطور نیست. به هر حال به خوبی با کتابخانه هایی مثل Chai و Expect.js کار می‌کند.

ما با استفاده از describe مجموعه ای از تست ها را با it تعریف کرده ایم. ما فرض کرده ایم که یک فایل JSONReader.js در دایرکتوری جاری وجود دارد. حال اجازه دهید تست خود را با mocha ./test.js اجرا کنیم. نتیجه بصورت زیر است:

$ Mocha test.js Testing json reader 1) Should get json 0 passing (31ms) 1 failing 1) Testing json reader should get json: Assertionerror: 'undefined' == 'function' +expected –Actual -undefined +function

واضح است که با چنین خطایی روبرو شدیم چون چنین فایلی وجود ندارد ، اگر فایل را ایجاد و کدهای پایین را به آن اضافه کنید، تست ما با موفقیت انجام می شود:

// JSONReader.js module.exports = { read: function() { // get JSON return {}; } }

ماژول JSONReader شی را export می کند که با استفاده از آن می توانیم به متد عمومی read دسترسی داشته باشیم. حال می توانیم دستور mocha .\test.js را اجرا کنیم.

$ mocha test.js Testing json reader Should get json 1passing (15 ms)

اجازه دهید فرض کنیم که ماژول JSONReader ما بزرگتر و بزرگتر می شود. متدهای جدید به آن اضافه می شود و توسعه دهنده های متفاوتی روی همان فایل کار می کنند. تست ما هنوز هم بررسی می‌کند که آیا ماژول موجود است و آیا تابع read آنجاست. این مهم است چون در جایی از پروژه ما، برنامه نویس از ماژول JSONReader استفاده خواهد کرد و انتظار دارد تابع read در دسترس باشد.

در این تست ما فقط چند دستور را اضافه کردیم. با این حال در پروژه های واقعی، چندین بلوک describe و it موجود است. در اغلب موارد، شرکت ها قبل از اینکه محصول خود را عرضه کنند تکیه بر مجموعه تست ها خود دارند. اگر تستی وجود دارد که با شکست روبرو شده است،آنها آن را منتشر نمی کنند. در فصل های بعدی از این کتاب، تست هایی را با هم می نویسیم.

الگوی Model-View-Controller

همیشه شروع یک پروژه جدید یا پیاده سازی یک ویژگی جدید دشوار بوده است. ما تنها نمی دانیم که چطور ساختار کدها، چه ماژول هایی نوشته شود و چطور آنها را با هم مرتبط کنیم بپردازیم. در چنین مواردی، ما اغلب به شیوه های شناخته شده اعتماد می کنیم.به عنوان مثال، الگو طراحی Model-View-Controller ثابت کرده است که یکی از مهمترین الگوهای موثر برای توسعه وب، به دلیل جدا بودن داده ها، کدها و نمایش می باشد. ما شبکه اجتماعی خود را بر اساس این الگو خواهیم ساخت.

Model : مدل بخشی است که داده ها را ذخیره می کند. وقتی تغییر در آن ایجاد شود باعث به روزرسانی View می شود.

View:این لایه معمولا بخشی است که کاربر می تواند آن مشاهده کند. ارتباط مستقیمی باModel دارد تا داده ها را نمایش دهد.

Controllerتعامل کاربر باعث کمک به Controller می شود (گاهی از طریق View).

آن می تواند دستوراتی به Model ارسال کند تا وضعیت را به روزرسانی کند. در برخی موارد، می تواند به View اطلاع دهد تا کاربر نمایش دیگری را مشاهده کند.

با این حال، در توسعه وب (مخصوصا وقتی کدها در مرورگر اجرا می شوند)، View و Controllerهمان توابع را به اشتراک می گذارند. بیشتر اوقات هیچ تفاوتی بین این دو وجود ندارد. در این تاب، کنترولرها همچنین با عناطر UI تعامل می‌ کنند.

اجازه دهید به محیط توسعه Node.js سوئیچ کنیم. برای ساده کردن مثال، ما کدهای خود را در فایل server.js قرار می دهیم )فایلی که در ابتدا این فصل ایجاد کردیم(. برنامه ما تنها یک کار انجام می دهد — مقدار یک متغیر ذخیره شده در حافظه را به روز رسانی می کند.

در رابطه با بحث ما، View قرار است HTML را تولید کند. آنگاه این HTML به مرورگر فرستاده خواهد شد:

var view = { render: function() { var html = ''; html += '<!DOCTYPE html>'; html += '<html>'; html += '<head><title>Node.js byexample</title></head>'; html += '<body>'; html += '<h1>Status ' + (model.status ? 'on' : 'off') + '</h1>'; html += '<a href="/on">switch on</a><br />'; html += '<a href="/off">switch off</a>'; html += '</body>'; html += '</html>'; res.writeHead(200, {'Content-Type': 'text/html'}); res.end(html + '\n'); } };

در این کد، یک شی جاوا اسکریپت وجود دارد که تنها یک متد render دارد. برای مقداردهی یک محتوای مناسب برای تگ H1 ، ما از یک model و متغیر status استفاده کرده ایم. در صفحه وب سایت

لینک ساده نیز دارم. اولی برای تغییر وضعیت model.status به true و دومی برای تغییر وضعیت model.status به false به کار می رود.شی Model نسبتا کوچک است. مثل View تنها یک متد دارد:

var model = { status: false, update: function(s) { this.status = s; view.render(); } };

توجه داشته باشید که Model باعث نمایش view می شود. در اینجا مهم است که بدانید Model نباید در مورد داده ای که در لایه View نمایش داده می شود بداند. تمام کاری که انجام می دهد، ارسال یک سیگنال به View برای نمایش به روز رسانی ها است.

آخرین بخش از این الگو Controller است. این را می توان به عنوان یک نقطه ورود به اسکریپتما در نظر گرفت. اگر یک سرور Node.js ایجاد می کنیم، این تابعی است که شی request و response را می پذیرد:

var http = require('http'), res; var controller = function(request, response) { res = response; if (request.url === '/on') { model.update(true); } else if (request.url === '/off') { model.update(false); } else { view.render(); } };

ما مقدار پارامتر response را در یک متغیر سراسری قرار داده ایم تا بتوان به آن از هر تابع دیگری دسترسی داشته باشیم.

این شبیه به مثالی است که در ابتدای فصل دیدیم جایی که ما از مشخصه request.url برای کنترل جریان نرم افزار استفاده کردیم. کد قبل باعث می شود وضعیت model با هر بار که کاربر آدرس های /on یا /off را مشاهده می کند تغییر کند. اگر غیر از این باشد تابع render از view فراخوانی می شود.

الگو MVC به خوبی با Node.js مناسب است. همانطور که دیدیم، می توان آن را به راحتی پیاده سازی کرد. از آنجایی که واقعا محبوب می باشد، ماژول ها و حتی فریم ورک ها از این مفهوم استفاده می‌کنند. در چند فصل بعدی خواهیم دید که الگو MVC چطور در مقیاس بزرگ برنامه ما کار می‌کند.

معرفی مفهوم REST AP

EST مخفف Representational State Transfer می‌باشد. با این تعریف می‌توان گفت، سرچشمه معماری در وب است. در عمل مجموعه قوانینی است که ارتباط Client-Server را ساده می کند. بسیاری از شرکت ها API های REST ارائه می‌دهند، چون ساده و بسیار مقیاس پذیر هستند.

برای درک بهتر از اینکه دقیقا REST به چه معناست، اجازه دهید یک مثال ساده را مرور کنیم. ما یک فروشگاه اینترنتی داریم که می‌خواهیم کاربران داخلی سیستم خود را مدیریت کنیم. ما منطق

back-end را در کنترولرهای مختلف پیاده سازی کرده‌ایم. ما می‌خواهیم این ویژگی‌ها را از طریق درخواست‌های HTTP راه اندازی کنیم. به عبارت دیگر، نیاز به یک رابط کاربری برای این controllerها داریم. کار را با برنامه ریزی URL ها برای دسترسی به سرور شروع می کنیم. اگر از معماریREST استفاده کنیم، آنگاه شاید به مسیرهای (routes) زیر نیاز

داشته باشیم:

درخواست GET برای /users برگشت دادن یک لیست از تمام کاربران داخل سیستم

درخواست POST برای /users ایجاد کاربر جدید

درخواست PUT برای /users/24 ویرایش داده ها کاربر با یک آی دی منحصر به فرد

درخواست DELETE برای /users/24 حذف پروفایل کاربر با یکی آی دی منحصر به فرد

یک منبع تعریف شده وجود دارد — user

URL چیزی است که عملیات REST را ساده می کند. درخواست GET برای بازیابی داده ، POST برای ذخیره، PUT برای ویرایش و DELETE برای حذف رکورد استفاده می شود.

برخی از بخش های شبکه اجتماعی کوچک ما در معماری REST پیاده سازی می شود. ما همچنین دارای کنترولرهایی هستیم که مسئولیت رسیدگی به چهار نوع از درخواست ها برای عملیات لازم می باشد. اما با این حال، قبل از اینکه به آن بخش از کتاب برسیم، اجازه دهید یک سرور ساده Node.js درست کنیم که درخواست های GET ، POST ، PUT ، DELETE را می پذیرد. کد زیر را در فایلی به نام server.js قرار دهید:

var http = require('http'); var url = require('url'); var controller = function(req, res) { var message = ''; switch (req.method) { case 'GET': message = "That's GET message"; break; case 'POST': message = "That's POST message"; break; case 'PUT': message = "That's PUT message"; break; case 'DELETE': message = "That's DELETE message"; break; } res.writeHead(200, {'Content-Type': 'text/html'}); res.end(message + '\n'); }; http.createServer(controller).listen(3000); console.log('Server running at http://localhost:3000/');

شی req شامل متد method می باشد. این به ما می گوید که نوع request چه بوده است. ما ابتدا سرور قبل را با node ./server.js اجرا می کنیم:

$ node server.JS Server running at http://localhost:3000

و نوع های مختلفی از درخواست ها را به آن ارسال می کنیم. به منظور تست، می‌ توانیم از دستور محبوب curl استفاده کنیم:

$CURL http://LocalHost:3000 That's GET Message

اجازه دهید یک درخواست پیچیده تر مانند PUT امتحان کنیم

$CURL –X PUT –d book="Node.JS by Example" http://LocalHost:3000 That'S PUT Message

ما متد درخواست را با گزینه –X تغییر دادیم. در ادامه ما متغیری به نام book را با مقدار Node.js by Example انتقال دادیم. با این حال هنوز سرور ما کدهایی را برای پردازش بر روی پارامترها ندارد. تابع زیر را به server.js اضافه کنید:

var qs = require('querystring'); var processRequest = function(req, callback) { var body = ''; req.on('data', function(data) { body += data; }); req.on('end', function() { callback(qs.parse(body)); }); }

کد، شی req می پذیرد و دارای یک تابع callback می باشد چون جمع آوری داده ها یک عملیات asynchronous می باشد. متغیر body با داده هایی که می آید مقدار دهی می شود. تابع callback با انتقال body اجرا می شود. controller خود را نیز ویرایش کنید:

var controller = function(req, res) { var message = ''; switch (req.method) { case 'GET': message = "That's GET message"; break; case 'POST': message = "That's POST message"; break; case 'PUT': processRequest(req, function(data) { message = "That's PUT message. You are editing " + data.book + " book."; res.writeHead(200, {'Content-Type': 'text/html'}); res.end(message + "\n"); }); return; break; case 'DELETE': message = "That's DELETE message"; break; } res.writeHead(200, {'Content-Type': 'text/html'}); res.end(message + '\n'); };



نظرات کاربران



عبارت امنیتی : حاصل 3 × 6 می شود :
متن پیام :