ROUTES

۲۲ شهریور ۱۳۹۳

میانبرهای کمکی تاریخچه در Shell

یکسری متغیرها و میانبرها در shell لینوکس هست که کار کردن باهاش رو خیلی ساده‌تر و باحال‌تر میکنه. این میانبرها همه با ! شروع میشن. یکسری از پرکاربردترین‌هاش رو اینجا میزارم.

متغیرهای کمکی

فرض کنید دستور قبلی که در ترمینال زدیم دستور du -h directory/example.rb بوده. با توجه به این دستور، یک مثال نوشتم و خروجی هر کدوم از متغیرهای کمکی در جدول زیر نمایش داده شده:

متغیر توضیحات نمونه خروجی
!! فراخوانی دستور قبل sudo !! sudo du -h directory/example.rb
!* هر چیزی غیر از نام دستور قبل - -h directory/example.rb
!:n جدا کردن قسمت n ام !:1 -h
!$ یا $_ آخرین قسمت دستور قبل، معمولا path !$ directory/example.rb
!$:h قسمت اول path دستور قبل !$:h directory
!$:t قسمت آخر path دستور قبل !$:t example.rb
!$:r path بدون پسوند پرونده !$:r directory/example
!$:t:r قسمت آخر path بدون پسوند پرونده !$:t:r example
!$:t:e نمایش فقط پسوند !$:t:e rb

نکته: یک توضیح در مورد دستورهایی که با !$ شروع میشن: می‌تونید به جای !$ از !:n استفاده کنید. مثلا در دستور نمونه ما path در قسمت دوم است. پس تمامی موارد بالا با !:2 هم کار میکنه. همیشه ممکنه path آخرین قسمت دستور نباشه! :)

کجا استفاده میشن؟

معمولا وقتی در دستور بعدی میخواهید به دستور قبلی ارجاع کنین و نمی‌خواید همه چیز رو دوباره تایپ کنید. مثلا اگر یادتون میره جلوی دستور بزنید sudo می‌تونید تو دستور بعد به جای تایپ کردن همه چیز فقط بنویسین sudo !!. یا اینکه در دستور بعد میخواین pathای که در دستور قبلی دادید رو دوباره استفاده کنید، یک راه‌اش اینه که کپی کنید و راه دیگه اینه که مثلا vim $_. راحت‌تر نیست؟

تفاوت Bash و Zsh

تنها تفاوت اینه که Bash بلافاصله دستور رو اجرا میکنه ولی Zsh دستور رو فقط نمایش میده. برای اینکه Bash هم خودش اجرا نکنه از :p استفاده کنید تا فقط نمایش داده بشه. مثلا: sudo !!:p. در کل اگه Bash استفاده می‌کنید برید زودتر Zsh استفاده کنید :D

اصلاح دستور قبل

فرض کنید در یک دستور غلط املایی دارین. مثلا du -h examlpe.rb. برای اصلاح‌اش کافیه دستور زیر رو بزنید:

^examlpe^example

shell دستور اصلاح شده رو نمایش میده.

۲۲ مرداد ۱۳۹۳

رمزنگاری و مخفی کردن فایل‌ها در لینوکس با Tomb

رمزنگاری و بک‌آپ از اطلاعات مهم در هر سیستمی اهمیت داره. لینوکس به‌طور پیش‌فرض از رمزنگاری پشتیبانی میکنه و ابزارهای خوبی رو برای اینکار در اختیار کاربر قرار داده. Tomb یک اسکریپت هست که انجام این کار رو بسیار راحت میکنه. Tomb یک پرونده رمزنگاری شده میسازه که میشه روی سیستم mount کرد و پرونده‌های مختلف رو توش قرار داد.

مزایای Tomb

  • پرونده ساخته شده با Tomb مثل یک پارتیشن معمولی است
  • پرونده Tomb با یک کلید (GnuPG) قابل باز شدن است
  • پرونده‌ی کلید رو میشه جدا از پرونده‌ی Tomb نگهداری کرد (مثلا روی فلش، روی یک سرور دیگه و...)
  • از اونجایی که مونت کردن یک پارتیشن احتیاج به دسترسی sudo داره، پس پسورد کاربر ریشه هم لازم هست
  • میشه تعداد زیادی پرونده Tomb با حجم‌های مختلف ساخت و همه رو مدیریت کرد

چقدر امنیت داره

تنها چیزی که میشه با اطمینان در موردش صحبت کرد مرگ هست. اما Tomb امن است. سورس Tomb همیشه باز هست و با یکم دانش اسکریپت نویسی Shell میشه تغییرش داد. تمام ابزارهایی که Tomb ازش استفاده میکنه به صورت پیش‌فرض در لینوکس وجود داره، و Tomb چیزی از خودش اضافه نمی‌کنه.

نصب

تو آرچ Tomb از طریق AUR قابل نصب هست. می‌تونید از yaourt یا هر مدیر بسته دیگه‌ای برای نصبش استفاده کنید. برای توزیع‌های دیگه هم به این صفحه در مستندات Tomb مراجعه کنید. Tomb برای نصب نیاز به ZSH داره.

نحوه کار

به طور خلاصه، اول باید یک پرونده Tomb با حجم دلخواه بسازید (به همین میزان حجم میتونید داخل این پرونده، پرونده‌های مختلف خودتون رو ذخیره کنید). بعد یک کلید می‌سازید و برای کلید هم یک رمزعبور تعیین می‌کنید. و در آخر این کلید رو به پرونده Tomb متصل می‌کنید تا از این به بعد این پرونده فقط از طریق این کلید و اون رمزعبور باز خواهد شد.

برای ساخت پرونده‌ی Tomb از دستور dig، و از کلید -a هم برای مشخص کردن حجم پرونده استفاده می‌کنیم (حجم به مگابایت):

$ tomb dig -s 128 secret.tomb

این یک پرونده Tomb میسازه به اسم secret.tomb با حجم ۱۲۸ مگابایت.

حالا یک کلید میسازیم:

$ tomb forge secret.tomb.key

با این دستور یک کلید میسازید و در نهایت هم از شما یک رمزعبور میخواد که باید به دلخواه وارد کنید.

کلید رو به پرونده متصل میکنیم:

$ tomb lock secret.tomb -k secret.tomb.key

حالا برای باز کردن پرونده از دستور open استفاده می‌کنیم. برای باز کردن پرونده باید پرونده GPG رو هم با کلید -k به فرمان بدیم:

tomb open secret.tomb -k secret.tomb.key

برای بستن (umount) کردن پرونده از دستور tomb close استفاده کنید. بعدا میشه به همین ترتیب برای تغییر اندازه‌ی پرونده از دستور resize استفاده کرد. برای اطلاعات بیشتر man رو بخونید.

۲۱ مرداد ۱۳۹۳

تگ کردن در Git

مثل خیلی از سیستم‌های مدیریت نسخه‌ی دیگه، گیت هم قابلیت تگ کردن و مشخص کردن یک قسمت مهم از تاریخچه نرم‌افزار رو داره. افراد از این امکان معمولا برای مشخص کردن انتشارها (v1.0 و...) استفاده میکنن. تو این پست لیست کردن تگ‌ها، ساختن تگ‌ها و انواع تگ‌های مختلف رو نشون میدم.

لیست کردن تگ‌ها

برای لیست کردن تگ‌ها از دستور git tag استفاده کنین:

$ git tag
v0.1
v1.3

این دستور تگ‌ها رو به ترتیب حروف الفبا نشون میده و ترتیب نمایش اونها ارتباطی به اهمیت‌شون نداره.

شما همینطور می‌تونین تگ‌ها رو براساس الگویی خاص فهرست کنید. مثلا در مخزنی که شامل ۲۴۰ تا تگ میشه و شما فقط به تگ‌های مربوط به نسخه 1.4.2 اهمیت میدین، میتونید دستور زیر رو بزنید:

$ git tag -l 'v1.4.2.*'
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4

ساخت تگ

در گیت دو نوع تگ کردن وجود داره: lightweight و annotated. تگ lightweight دقیقا مثل یک شاخه هست که تغییری نمیکنه - فقط یک نشانگر هست به یک کامیت خاص. ولی تگ‌های annotated یک آبجکت کامل رو در بانک‌اطلاعاتی گیت ذخیره میکنن. اونها checksum دارن، اسم، ایمیل تگ کننده رو نشون میدن و تاریخ هم دارن و میتونن با GPG هم امضا بشن. به طور معمول توصیه میشه که از تگ‌های annotated استفاده کنید چون حاوی تمام اطلاعات هستن.

تگ‌های Annotated

ساخت تگ‌های annotated ساده است. از کلید -a با فرمان tag استفاده کنید:

$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4

کلید -m پیام تگ رو مشخص میکنه. اگر این کلید رو استفاده نکنید گیت ادیتور پیش‌فرض انتخاب شده‌تون رو نمایش خواهد داد تا پیام تگ رو وارد کنید. با ذخیره کردن و خروج از ادیتور عمل تگ انجام میشه.

تگ‌های امضا شده

برای امضا کردن تگ‌هاتون با کلید GPG، تنها کاری که باید بکنید استفاده از کلید -s به جای -a است:

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <[email protected]>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

اگر دستور git show رو اجرا کنید، می‌تونید امضا GPG خودتون رو ببینید.

تگ‌های Lightweight

برای این تگ‌ها از کلیدهای -m، -s یا -a استفاده نکنید:

$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5

اگر اینبار دستور git show رو اجرا کنید فقط همون کامیت رو می‌بینید و اطلاعات بیشتری وجود نداره:

$ git show v1.4-lw
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: Scott Chacon <[email protected]>
Date:   Sun Feb 8 19:02:46 2009 -0800

    Merge branch 'experiment'

برای اطلاعات بیشتر در مورد تگ‌ها در گیت به مستندات گیت مراجعه کنید.

۱۰ مرداد ۱۳۹۳

گرفتن زیرشاخه‌های خاص از یک مخزن Git

نوشته از Jason Karns در Code Nomad

ممکنه یک زمانی بخواهید از یک مخزن Git فقط یک قسمت/پوشه خاص رو دریافت کنین و آپدیت نگه‌اش دارین. برای اینکار باید از امکان spare-checkout گیت استفاده کنید.

مخزن جدید

برای اینکه بشه از این امکان استفاده کرد، باید تو تنظیمات مخزن گیت core.sparecheckout مقدار true تظیم بشه. برای اینکار هم لازمه‌اش اینه که مخزن از قبل وجود داشته باشه. پس به جای اینکه git clone کرد، باید git init انجام داد.

مخزن جدید رو بسازید:

mkdir <repo> && cd <repo>
git init
git remote add –f <name> <url>

امکان spare-checkout رو فعال کنید:

git config core.sparsecheckout true

زیر شاخه‌هایی که میخواین رو در .git/info/spare-checkout اضافه کنید:

echo some/dir/ >> .git/info/sparse-checkout
echo another/sub/tree >> .git/info/sparse-checkout

حالا مخزن رو از سرور pull کنید:

git pull <remote> <branch>

مخزن موجود

اگر از قبل یک مخزن دارید و میخواید spare-checkout استفاده کنید، مثل مورد بالا spare-checkout رو فعال کنین و بعد هم git read-tree رو انجام بدین:

git config core.sparsecheckout true

اضافه کردن شاخه‌های مورد نیاز به .git/info/spare-checkout :

echo some/dir/ >> .git/info/sparse-checkout
echo another/sub/tree >> .git/info/sparse-checkout

مخزن رو آپدیت کنین:

git read-tree -mu HEAD

تغییر زیرشاخه‌های انتخاب شده

اگر بعداً تصمیم گرفتین که زیرشاخه‌هایی که انتخاب کردین رو تغییر بدین، خیلی راحت پرونده spare-checkout رو تغییر بدین و بعد هم git read-tree رو دوباره اجرا کنین.

مستندات مربوط به sub-tree/spare-checkout رو بخونید. پرونده spare-tree از الگوها هم پشتیبانی می‌کنه، دقیقا مثل gitignore. از لیست سیاه هم پشتیبانی میشه - میتونید انتخاب کنید که چه شاخه‌ها یا پرونده‌های checkout نشن.

۸ مرداد ۱۳۹۳

شروع کار با Gulp

نوشته از Mark Goodyear از اینجا

اگر روبی کار باشید، احتمالا با Guard آشنا هستین. به طور خلاصه Gulp همون Guard دنیای جاوا اسکریپت است. Gulp سیاست کد-دربرابر-پیکربندی داره. ینی برخلاف Grunt که برای تنظیم باید از کلیدهای پیش‌فرض خودش استفاده کنید و پیکربندی‌اش کنید، Gulp در واقع همون کد زدن برای node.js هست. بنابراین میشه از همه کتابخانه‌های npm در فایل تنظیمات Gulp استفاده کرد.

Gulp از استریم‌های node.js استفاده میکنه که باعث میشه buildها سریع‌تر ساخته بشن و برای ساخت اونها احتیاجی به ایجاد پرونده‌ها و پوشه‌های موقت نیست. اگر می‌خواهید بیشتر در مورد استریم‌های بدونید - با اینکه احتیاجی نیست - میتونید این مقاله رو بخونید. Gulp، برعکس Grunt که باید پیکربندی هر پلاگین رو بهش بدید، به شما اجازه میده که پرونده‌های پروژه‌تون رو بهش بدید، Gulp از طریق pipeها اونها رو از پلاگین‌هاش عبور میده و خروجی رو در نهایت به شما ارائه میکنه. بزارید یک مثال برای ساخت پرنده Sass در هر دو ابزار Grunt و Gulp رو ببینیم تا تفاوتشون بیشتر درک بشه:

Grunt

sass: {
  dist: {
    options: {
      style: 'expanded'
    },
    files: {
      'dist/assets/css/main.css': 'src/styles/main.scss',
    }
  }
},

autoprefixer: {
  dist: {
    options: {
      browsers: [
        'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
      ]
    },
    src: 'dist/assets/css/main.css',
    dest: 'dist/assets/css/main.css'
  }
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

برای Gulp باید هر پلاگین رو جداگانه پیکربندی کنید، پرونده‌های ورودی و محل خروجی رو هم براش مشخص کنید. برای مثال، ما اول باید یک پرونده ورودی رو به پلاگین Sass بدیم، که بعد پرنده خروجی رو برمیگردونه. بعد از اون باید خروجی پلاگین Sass رو به پلاگین Autoprefixer بدیم، که یک فایل دیگه رو به خروجی میده. حالا همین کار رو با Gulp انجام میدیم:

Gulp

gulp.task('sass', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'compressed' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

با Gulp ما یکبار پرونده رو بهش میدیم. این پرونده توسط پلاگین Sass تغییر داده میشه، بعد پلاگین Autoprefixer ارسال میشه و تغییر داده میشه و در نهایت یک پرونده خارج میشه. این پروسه سرعت ساخت پرونده نهایی رو بیشتر میکنه، چون احتیاجی به ساخت پرونده‌های موقت مختلف برای تولید یک پرونده نهایی نیست.

خب. حالا که فهمیدیم Gulp چقدر خوب‌تره، نصبش می‌کنیم و چندتا کار مختلف باهاش انجام میدیم.

نصب Gulp

برای نصب عبارت زیر رو تو ترمینال وارد کنید. ممکنه احتیاج باشه sudo رو هم وارد کنید:

$ npm install gulp -g

این باعث میشه دسترسی به فرمان gulp رو در همه جای سیستم داشته باشیم. بعد از این Gulp باید به شکل محلی در مکان پروژه هم نصب بشه. cd کنید در محل پروژه و فرمان زیر رو اجرا کنید (مطمئن بشید که یک پرونده package.jsonدارید):

$ npm install gulp --save-dev

این فرمان Gulp رو به صورت محلی در پروژه شما نصب میکنه و اون رو در devDependencies در پرونده package.json اضافه میکنه.

نصب پلاگین‌های Gulp

ما تعدادی پلاگین برای انجام کارهای زیر رو نصب می‌کنیم:

برای نصب این پلاگین‌ها فرمان زیر رو بزنید:

$ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-clean gulp-notify gulp-rename gulp-livereload gulp-cache --save-dev

این فرمان تمام پلاگین‌های مورد نیاز رو نصب و بد هم در قسمت devDependencies پرونده package.json اضافه می‌کنه. فهرست کامل پلاگین‌های Gulp رو می‌تونید اینجا ببینید.

بارگیری پلاگین‌ها

بعد از این لازم هست که یک پرونده gulpfile.js بسازیم و پلاگین‌هامون رو بهش معرفی کنیم:

var gulp = require('gulp'),
    sass = require('gulp-ruby-sass'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    clean = require('gulp-clean'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload');

پلاگین‌های Gulp متفاوت از پلاگین‌های Grunt هستن. اونها طوری نوشته شدن که فقط یک کار انجام بدن و اون کار رو درست و خوب انجام بدن (سیاست برنامه‌های گنو/لینوکسی هست درواقع اینجا :دی). مثلا، imagemin در Grunt از کش کردن برای فشرده نکردن عکس‌هایی که قبلا فشرده شدن استفاده میکنه. با Gulp، اینکار با یک پلاگین cache انجا میشه، که میشه برای کش کردن چیزهای دیگه هم ازش استفاده کرد. این کار انعطاف‌پذیری بیشتری به پروسه ساخت میده.

میشه پلاگین‌ها رو خودکار بارگیری کرد، اما برای این پست ما به همین روش دستی قناعت می‌کنیم!

ساخت وظیفه‌ها (Task)

کامپایل پرونده Sass، استفاده از Autoprefix و کوچک کردن پرونده‌ها

اول از همه، ما کامپایل Sass رو پیکربندی می‌کنیم. ما پرونده Sass رو به شکل expanded کامپایل می‌کنیم، بعد می‌فرستیمش برای Autoprefixer و در محل مورد نظرمون قرارش می‌دیم. بعد از اون یک نسخه .min هم ازش می‌سازیم، صفحه مرورگر رو به طور خودکار دوباره بارگیری می‌کنیم بعد هم یک اعلان به ما خبر میده که کار Gulp تموم شده:

gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'expanded' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/assets/css'))
    .pipe(notify({ message: 'Styles task complete' }));
});

کمی توضیحات بیشتر، قبل از اینکه ادامه بدیم.

gulp.task('styles', function() { ... )};

این API مربوط به gulp.task هست که برای ساخت هر وظیفه مختلف Gulp استفاده میشه. وظیفه‌ای که بالا ساختیم رو می‌تونیم با عبارت $ gulp styles اجرا کنیم.

return gulp.src('src/styles/main.scss')

سینتکس مربوط به gulp.src که پرونده‌های ورودی رو باهاش مشخص می‌کنیم. ورودی این قسمت می‌تونه یک الگو کلی مثل /**/*.scss باشه. با برگردوندن (return) استریم، وظیفه‌ای که تعریف کردیم asynchronous میشه و مطمئن میشیم که وظیفه قبل از اعلانِ پیامِ اتمام، تمام کارها رو کامل انجام داده.

.pipe(sass({ style: 'expanded' }))

ما از .pipe() برای انتقال پرونده‌ها به پلاگین‌ها استفاده می‌کنیم. معمولا گزینه‌های مربوط به هر پلاگین رو میتونید صفحه گیت‌هابشون ببینید. من لینک‌های اونها رو بالا براتون گذاشتم.

.pipe(gulp.dest('dist/assets/css'));

سینتکس بالا مربوط به gulp.dest است که آدرس خروجی رو بهش میدیم. هر وظیفه می‌تونه تعدادی بیشتر از یک دونه خروجی داشته باشه. مثلا یک خروجی پرونده expanded رو ذخیره می‌کنه و خروجی دیگه پرونده خلاصه شده. این کار رو ما در مثال بالا انجام دادیم.

من پیشنهاد می‌کنم مستندات API مربوط به Gulp رو بخونید تا درک بهتری از API داشته باشین. اونقدری که به نظر میاد ترسناک نیست!

استفاده از JSHint و concat و خلاصه کردن جاوااسکریپت

احتمالا تا الان نحوه نوشتن یک وظیفه برای Gulp رو یاد گرفتین. در ادامه، ما کدها رو برای lint و concat و همچنین uglify اضافه می‌کنیم:

gulp.task('scripts', function() {
  return gulp.src('src/scripts/**/*.js')
    .pipe(jshint('.jshintrc'))
    .pipe(jshint.reporter('default'))
    .pipe(concat('main.js'))
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(gulp.dest('dist/assets/js'))
    .pipe(notify({ message: 'Scripts task complete' }));
});

یک نکته اینکه ما باید برای JSHint از یک گزارشگر استفاده کنیم. من از همون گزارشگر پیش‌فرض استفاده می‌کنم که احتمالا برای اکثر توسعه‌دهنده‌ها کافی است. اطلاعات بیشتر رو می‌تونید تو سایت JSHint ببینید.

فشرده کردن عکس‌ها

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

gulp.task('images', function() {
  return gulp.src('src/images/**/*')
    .pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
    .pipe(gulp.dest('dist/assets/img'))
    .pipe(notify({ message: 'Images task complete' }));
});

این کد تمام عکس‌ها رو میگیره و بعد میفرسته برای پلاگین imagemin. میشه بیشتر ادامه داد و از امکان کش کردن برای اینکه عکس‌های فشرده شده رو دوباره فشرده‌سازی نکنیم، استفاده کرد. برای اینکار فقط به پلاگین gulp-cache احتیاج داریم - که قبلا نصبش کردیم. برای اینکار باید خط زیر رو:

.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))

به شکل زیر اصلاح کنیم:

.pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true })))

حالا فقط عکس‌هایی که فشرده نشدن یا جدید هستن فشرده میشن. باحاله!

تمیزکاری!

قبل از فرستادن پروژه به سرور، بهتره که پوشه‌های مقصدمون رو تمیز کنیم و پرونده‌ها رو دوباره بسازیم - برای اینکه مطمئن بشیم که پرونده‌هایی که ممکنه از سورس اصلی حذف کردیم، نسخه ساخته شدشون تو پوشه مقصد نباشه هنوز:

gulp.task('clean', function() {
  return gulp.src(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], {read: false})
    .pipe(clean());
});

ما حتی می‌تونیم یک آرایه از پوشه‌ها (یا پرونده‌ها) رو به gulp.src بدیم. با توجه به اینکه احتیاجی نیست پرونده‌هایی که حذف شدن رو بخونیم، میتونیم read: false رو به گزینه‌ها اضافه کنیم تا به Gulp بگیم احتیاجی به خوندن محتویات پرونده‌ها نیست - کمی سرعتش بیشتر میشه.

تعریف وظیفه پیش‌فرض

می‌تونیم یک وظیفه پیش‌فرض اضافه کنیم تا وقتی دستور $ gulp رو میزنیم اجرا بشه و هر سه وظیفه‌ای که برای Gulp تعریف کردیم رو اجرا کنه:

gulp.task('default', ['clean'], function() {
    gulp.start('styles', 'scripts', 'images');
});

به آرایه‌ای که به gulp.task اضافه شدن دقت کنین. اینجا جایی هست که می‌تونیم وابستگی‌های این وظیفه رو مشخص کنیم. اینطوری وظیفه‌ی clean، که قبلا تعریف کردیم، پیش از وظیفه‌های داخل gulp.start اجرا میشه. وظیفه‌ها داخل Gulp به شکل همروند (concurrent) باهم اجرا میشه و هیچ ترتیبی که کدوم زودتر تموم میشه وجود نداره، بنابراین لازمه که مطمئن بشیم که clean قبل از بقیه اجرا شده.

پاییدنِ پرونده‌ها!

برای اینکه تغییرات پرونده‌ها رو زیرنظر بگیریم و وظیفه‌های لازم رو وقتی که تغییر کردن خودکار اجرا کنیم، باید اول یک وظیفه جدید تعریف کنیم، بعد با gulp.watch‍ تغییرات پرونده‌ها رو زیر نظر بگیریم:

gulp.task('watch', function() {

  // Watch .scss files
  gulp.watch('src/styles/**/*.scss', ['styles']);

  // Watch .js files
  gulp.watch('src/scripts/**/*.js', ['scripts']);

  // Watch image files
  gulp.watch('src/images/**/*', ['images']);

});

ما پرونده‌هایی که میخوایم زیر نظر بگیریم رو با استفاده از gulp.watch تعریف می‌کنیم. حالا می‌تونیم دستور $ gulp watch رو اجرا کنیم و با هر تغییری تو پرونده‌هامون، Gulp وظیفه‌اش رو انجام میده!

بارگیری خودکار صفحه در مرورگر

Gulp می‌تونه صفحه مرورگر شما رو وقتی پرونده‌ها تغییر میکنن خودش دوباره بارگیری کنه. فقط لازمه که وظیفه‌ی watch رو که بالا نوشتیم رو کمی تغییر بدیم و سرور LiveReload رو بهش اضافه کنیم:

gulp.task('watch', function() {

  // Create LiveReload server
  var server = livereload();

  // Watch any files in dist/, reload on change
  gulp.watch(['dist/**']).on('change', function(file) {
    server.changed(file.path);
  });

});

سرهم‌بندی کد

این کل پرونده gulp که نوشتیم:

// Load plugins
var gulp = require('gulp'),
    sass = require('gulp-ruby-sass'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    clean = require('gulp-clean'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload');

// Styles
gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'expanded', }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/styles'))
    .pipe(rename({ suffix: '.min' }))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/styles'))
    .pipe(notify({ message: 'Styles task complete' }));
});

// Scripts
gulp.task('scripts', function() {
  return gulp.src('src/scripts/**/*.js')
    .pipe(jshint('.jshintrc'))
    .pipe(jshint.reporter('default'))
    .pipe(concat('main.js'))
    .pipe(gulp.dest('dist/scripts'))
    .pipe(rename({ suffix: '.min' }))
    .pipe(uglify())
    .pipe(gulp.dest('dist/scripts'))
    .pipe(notify({ message: 'Scripts task complete' }));
});

// Images
gulp.task('images', function() {
  return gulp.src('src/images/**/*')
    .pipe(cache(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })))
    .pipe(gulp.dest('dist/images'))
    .pipe(notify({ message: 'Images task complete' }));
});

// Clean
gulp.task('clean', function() {
  return gulp.src(['dist/styles', 'dist/scripts', 'dist/images'], {read: false})
    .pipe(clean());
});

// Default task
gulp.task('default', ['clean'], function() {
    gulp.start('styles', 'scripts', 'images');
});

// Watch
gulp.task('watch', function() {

  // Watch .scss files
  gulp.watch('src/styles/**/*.scss', ['styles']);

  // Watch .js files
  gulp.watch('src/scripts/**/*.js', ['scripts']);

  // Watch image files
  gulp.watch('src/images/**/*', ['images']);

  // Create LiveReload server
  var server = livereload();

  // Watch any files in dist/, reload on change
  gulp.watch(['dist/**']).on('change', function(file) {
    server.changed(file.path);
  });

});

این پرونده رو می‌تونید تو gist هم ببینید. من این پیکربندی رو برای Grunt هم انجام دادم که می‌تونید برای مقایسه، تو همون gist ببینیدش.

اگر هر سوالی داشتید از فیلدهای نظردهی استفاده کنید.

۲۲ اردیبهشت ۱۳۹۳

تفاوت alias و alias_method در روبی

تو روبی برای ایجاد نام مستعار برای متدها میشه از alias یا alias_method استفاده کرد. در نگاه اول ممکنه به نظر بیاد که این دو تا مثل هم هستن ولی در حقیقت یک تفاوت خیلی کوچک با هم دارن.

کد برای alias:

class Test
  def name
    puts 'Arash'
  end

  alias :sirname :name
end

Test.new.name # => Arash
Test.new.sirname # => Arash

کد برای alias_method:

class Test
  def name
    puts 'Arash'
  end

  alias_method :sirname, :name
end

Test.new.name # => Arash
Test.new.sirname # => Arash

تا اینجا که چیز خیلی خاصی دیده نمیشه. فقط اینکه تو alias از کاما استفاده نمی‌کنیم. این رفتار عادی و مورد انتظار از alias و alias_method است. در این موارد name و sirname هر دو خروجی یکسان دارن. اما اگه یک کلاس زیرمجموعه از Test درست کنیم چی؟

class Test
  def name
    puts 'Arash'
  end

  def self.do_rename
    alias :sirname :name
  end
end

class Example < Test
  def name
    puts 'Ernie'
  end
  do_rename
end

Example.new.name # => Ernie
Example.new.sirname # => Arash

اینجا sirname به کلاس والد اشاره می‌کنه. alisa زمانی که کد parse میشه مقداردهی میشه. موقع اجرای Test متد sirname با مقدار name پر میشه و این برای تمام کلاس‌های گرفته شده از Test هم درسته.

در طرف مقابل alias_method در همون حوزه‌ی self مقداردهی میشه:

class Test
  def name
    puts 'Arash'
  end

  def self.do_rename
    alias_method :sirname, :name
  end
end

class Example < Test
  def name
    puts 'Ernie'
  end

  do_rename
end

Example.new.name # => Ernie
Example.new.sirname # => Ernie

توضیح خاص دیگه فکر نکنم بخواد. کاملا روشن‌ه :)

۲۹ فروردین ۱۳۹۳

Rails 4.1 ActiveRecord enums

نسخه ۴.۱ ریلز به تازگی منتشر شده و امکانات جالب جدیدی داره. یکی از این امکانات ActiveRecord Enums هست، یک امکان خوب که ذخیره حالت‌های مختلف یک رکورد در مدل رو آسون میکنه.

مثلا فرض کنید که در برنامه‌تون یک مدل User دارین و هر کدوم از این کاربرها می‌تونین یک وضعیت به عنوان‌های registered و active یا blocked داشته باشن.

تو نسخه‌های قبلی اگر میخواستین این کار رو بکنید، احتمالا یک فیلد متنی یا عددی به دیتابیس اضافه می‌کردین و بعد هم چندتا scope توی مدل میذاشتین تا از جدول اطلاعات رو بگیرید. حالا همه چیز ساده‌تر شده. فقط لازمه که یک migration بنویسید و فقط فیلد لازم رو به جدول اضافه کنید:

class AddStatus < ActiveRecord::Migration
  def change
    add_column :users, :state, :integer
  end
end

و بعد هم ماکرو enum رو به کلاس User اضافه کنید:

class User
  enum state: [:registered, :active, :blocked]
end

به صورت پیش‌فرض وضعیت کاربر nil خواهد بود. ما می‌تونیم با اسم هر وضعیت یک کوئری بگیریم:

user = User.new
user.state
 # => nil

user.registered?
 # => false

user.state = :registered
user.registered?
 # => true

می‌تونیم مستقیما وضعیت کاربر رو بروزرسانی و ذخیره کنیم:

user.registered!
user.persisted?
 # => true
user.registered?
 # => true

همینطور به طور خودکار یک scope هم برای هر وضعیت داریم:

User.active
 # => #<ActiveRecord::Relation []>
User.registered
 # => #<ActiveRecord::Relation [#<User id: 7, status: 0...]>

حتی می‌تونیم به شکل مستقیم یک کاربر با وضعیتی که میخوایم بسازیم:

User.registered.create
 # => #<User id: 6, status: 1, ...>

ActiveRecord برای ذخیره وضعیت‌ها توی فیلد دیتابیس از عدد استفاده میکنه. اگر لازم باشه که یک وضعیت پیش‌فرض برای کاربر داشته باشیم (ینی کاربری که ساخته میشه مثلا وضعیت active داشته باشه و نه nil) میتونیم توی migration از کلید default استفاده کنیم:

class ChangeStatus < ActiveRecord::Migration
  def change
    change_column :users, :status, :integer, default: 1
  end
end

از الان به بعد تمام کاربرهای جدیدی که ساخته میشن وضعیت پیش‌فرض active رو خواهند داشت:

user = User.new
user.state
 # => "active"

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

class User
  enum state: [:logger]
end
 # => ArgumentError: You tried to define an enum named "state" on the model "User",
 # but this will generate a class method "logger", 
 # which is already defined by Active Record.

۲۴ اسفند ۱۳۹۲

چرا نباید از دکمه Snooze آلارم استفاده کرد

تو ویدئو زیر به شکلی علمی توضیح میده که چرا نباید از امکان Snooze کردن آلارم استفاده کرد.

Don't Snooze

ح.ز.ب.ن (حجمش زیاد بود نگرفتم):

خلاصه اینکه اگه یک زمان خواب مرتب نداشته باشین آلارم ساعت باعث میشه چرخه خواب شما مختل بشه و زمانی که بدن لازم داره تا وضعیت‌تون رو به حالت سرحال تغییر بده رو کوتاه میکنه. استفاده از Snooze که مثلا هر ده دقیقه صدای زنگ درمیاد باعث میشه این چرخه حتی کوتاه‌تر بشه و همیشه بعد از بیدار شدن احساس خستگی بکنید و روز خوبی نداشته باشید. بهتر اینه که زمان خواب مرتبی تنظیم کنید و ساعتتون رو هم زمانی که باید بیدار بشید کوک کنید. بعد از یک هفته بدنتون خودش رو باهاش تطبیق میده و روز رو خیلی بهتر آغاز می‌کنید.