مطالب
چگونه یک اسکریپت گریس مانکی بنویسیم؟

گریس مانکی یکی از افزونه‌های فایرفاکس است که توسط آن می‌توان اسکریپت‌هایی را بر روی صفحات وب در حال مشاهده، جهت تغییر آن‌ها اجرا کرد. این نوع تغییرات بیشتر در جهت بالا بردن کارآیی یا خوانایی سایت‌ها صورت می‌گیرد. مثلا بررسی وجود لینک‌های ارائه شده در یک صفحه وب (قبل از اینکه به آن‌ها رجوع کنیم، مشخص شود که آیا وجود دارند یا خیر) و هزاران مثال شبیه به این که در سایت اسکریپت‌های آن قابل دریافت هستند.
گریس مانکی به خودی خود کار خاصی را انجام نمی‌دهد و فقط میزبان اجرایی اسکریپت‌هایی است که برای آن تهیه شده اند. به این اسکریپت‌ها user scripts گفته می‌شود و جهت تهیه آن‌ها از زبان جاوا اسکریپت استفاده می‌گردد.
مطابق اصول نامگذاری آن، فایل این اسکریپت‌ها حتما باید به .user.js ختم شود تا توسط افزونه گریس مانکی قابل شناسایی باشد.

اسکریپت سلام دنیای گریس مانکی!
// ==UserScript==
// @name Hello World
// @namespace http://diveintogreasemonkey.org/download/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==
alert('Hello world!');

فرمت کلی و آناتومی یک اسکریپت گریس مانکی مطابق چند سطر فوق است. در ابتدا نامی را که برای اسکریپت مشخص کرده‌اید، ذکر خواهید نمود. سپس فضای نام آن مشخص می‌گردد. این فضای نام یک آدرس وب خواهد بود (مثلا سایت شخصی شما) . به این طریق گریس مانکی می‌تواند اسکریپت‌های هم نام را بر اساس این فضاهای نام مختلف مدیریت کند. سپس توضیحات مربوط به اسکریپت ارائه می‌شود. در قسمت include مشخص خواهید کرد که این اسکریپت بر روی چه سایت‌ها و آدرس‌هایی اجرا شود و در قسمت exclude سایت‌های صرفنظر شونده را تعیین خواهید نمود. در مثال فوق، اسکریپت بر روی تمامی سایت‌ها اجرا خواهد شد، منهای دو سایت و زیر سایت‌هایی که در قسمت exclude مشخص شده‌اند.
در پایان مثلا نام آن‌را hello.user.js خواهیم گذاشت (همانطور که ذکر شد قسمت user.js آن باید رعایت شود). برای نصب آن فقط کافی است این فایل را به درون پنجره فایر فاکس کشیده و رها کنیم (drag & drop).

استفاده از jQuery در اسکریپت‌های گریس مانکی

عمده کاربردهای اسکریپت‌های گریس مانکی در جهت اعمال تغییرات بر روی document object model صفحه (DOM) هستند. کتابخانه jQuery اساسا برای این منظور تهیه و بهینه سازی شده است.

مثال:
فرض کنید قصد داریم به نتایج خروجی جستجوی گوگل، fav icon سایت‌های یافت شده را اضافه کنیم (در کنار هر لینک، آیکون سایت مربوطه را نمایش دهیم). گوگل این‌کار را انجام نداده است. اما ما علاقمند هستیم که این قابلیت را اضافه کنیم!
// ==UserScript==
// @name Google FavIcon
// @namespace http://userscripts.org
// @description Shows favicon for Google searches results
// @include http://*.google.*/search?*

// ==/UserScript==

loadJquery();

$(document).ready(function(){
$("h3.r > a.l").each(function(){
var $a = $(this);
var href = $a.attr("href");
var domain = href.replace(/<\S[^><]*>/g, "").split('/')[2];
var image = '<img src="http://' + domain + '/favicon.ico" style="border:0;padding-right:4px;" />';
//GM_log(">image:> " + image);
$(this).prepend(image);
});

});

نحوه پیاده سازی تابع loadJquery را در سورس مربوطه می‌توانید مشاهده نمائید.
دریافت سورس این مثال

در اینجا ابتدا لینک‌های حاصل از جستجو پیدا می‌شوند. سپس نام دومین مربوطه استخراج می‌گردد (با استفاده از regular expressions) و در ادامه از این نام دومین یک آدرس استاندارد http://domain/favico.ico ساخته شده و از آن یک تگ img درست می‌شود. در آخر این تگ به قبل از لینک‌های گوگل اضافه می‌شود.

شاید سؤال بپرسید از کجا مشخص شد که باید به دنبال h3.r > a.l گشت؟ به تصویر زیر دقت نمائید (نمایی است از یکی از توانایی‌های افزونه fireBug فایرفاکس).



هنگامیکه صفحه بارگذاری شد، بر روی آیکون سوسک موجود در status bar فایرفاکس کلیک کنید تا FireBug ظاهر شود (البته من اینجا سوسک دیدم شاید موجود دیگری باشد :) )
سپس بر روی دکمه inspect در نوار ابزار آن کلیک کنید. در همین حال اشاره‌گر ماوس را به یکی از لینک‌های نتیجه جستجوی گوگل نزدیک کنید. بلافاصله تگ‌ها و کلاس‌های مورد استفاده آن به شکل زیبایی ظاهر خواهند شد. به این صورت صرفه جویی قابل ملاحظه‌ای در وقت صورت خواهد گرفت.

نتیجه اجرای اسکریپت فوق (پس از نصب) به صورت زیر است:



نکته: نحوه دیباگ کردن اسکریپت‌های گریس مانکی

اگر نیاز به مشاهده مقدار متغیرها در لحظه اجرای اسکریپت داشتید، یکی از راه‌حل‌های موجود استفاده از تابع GM_log مربوط به API گریس مانکی است که خروجی آن‌را در قسمت messages مربوط به error console فایرفاکس می‌توان دید.



نظرات مطالب
استفاده از Kendo UI TreeView به همراه یک منبع داده راه دور
مطابق مستندات KendoUI، برای ایجاد tree view از دو روش می‌توانیم استفاده کنیم:
  • تعریف ساختار درختی به صورت استاتیک HTML
  • با استفاده از یک منبع راه دور 
بنده از حالت اول استفاده می‌کنم، یعنی یک ساختار ul li را در ویو تولید میکنم و در نهایت نتیجه به صورت زیر خواهد بود:
<ul id="treeview">
    <li>
Parent 1

        <ul>
            <li>Child</li>
            <li>Child</li>
            <li>Child</li>
        </ul>
    </li>
    <li>Parent 2</li>
    <li>
Parent 3

        <ul>
            <li>Child</li>
            <li>Child</li>
        </ul>
    </li>
    <li>Parent 4</li>
    <li>Parent 5</li>
    <ul>

 برای فعال‌سازی tree view نیز:
<link href="~/Content/kendo.common.min.css" rel="stylesheet"/>
<link href="~/Content/kendo.default.min.css" rel="stylesheet"/>
<link href="~/Content/kendo.dataviz.min.css" rel="stylesheet" />
<link href="~/Content/kendo.dataviz.default.min.css" rel="stylesheet"/>
<link href="~/Content/kendo.rtl.min.css" rel="stylesheet"/>
<script src="~/Scripts/kendo.all.min.js"></script>
<script type="text/javascript">
        $(document).ready(function() {
            $("#treeView").kendoTreeView({
                checkboxes: {
                    checkChildren: true
                }
            });
        });

</script>
ساختار tree view به خوبی نمایش داده می‌شود، اما هیچکدام از CheckBoxها نمایش داده نمی‌شوند. به نظر شما مشکل از کجا می‌تواند باشد.
با تشکر
نظرات مطالب
EF Code First #12
با سلام و تشکر از پاسختون
من با ViewModel آشنایی دارم و مطالب Mvc شما رو کامل خوندم آیا درست متوجه شدم جهت اعتبار سنجی باید یک کلاس در ViewModel بسازم و اعتبارسنجی رو انجام بدیم و بعد کلاس ViewModel رو تبدیل به کلاس اصلی کرده و برای انجام عملیات مربوطه به Service بفرستیم؟ یا نه باید یک کلاس مشابه کلاس موجود در DomainClasses در Model بسازم و اعتبارسنجی رو اونجا انجام بدم و بعد ارسال به Service
یا باید  به صورت زیر توی همون domainClasses انجام بدم
[MetadataType(typeof(CustomerMetadata))]
public partial class Customer
{
    class CustomerMetadata
     {
     }
}
public partial class Customer : IValidatableObject
{

مطالب
عبارت using و نحوه استفاده صحیح از آن
مثال ساده زیر را که در مورد تعریف یک کلاس Disposable و سپس استفاده از آن توسط عبارت using است را به همراه سه استثنایی که در این متدها تعریف شده است، در نظر بگیرید:
using System;

namespace TestUsing
{
    public class MyResource : IDisposable
    {
        public void DoWork()
        {
            throw new ArgumentException("A");
        }

        public void Dispose()
        {
            throw new ArgumentException("B");
        }
    }

    public static class TestClass
    {
        public static void Test()
        {
            using (MyResource r = new MyResource())
            {
                throw new ArgumentException("C");
                r.DoWork();
            }
        }
    }
}
به نظر شما قطعه کد زیر چه عبارتی را نمایش می‌دهد؟ C یا B یا A؟
try
{
     TestClass.Test();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

پاسخ: برخلاف تصور (که احتمالا C است؛ چون قبل از فراخوانی متد DoWork سبب بروز استثناء شده است)، فقط B را در خروجی مشاهده خواهیم کرد!
و این دقیقا مشکلی است که در حین کار با کتابخانه iTextSharp برای اولین بار با آن مواجه شدم. روش استفاده متداول از iTextSharp به نحو زیر است:
using (var pdfDoc = new Document(PageSize.A4))  
{  
   //todo: ...
}
در این بین هر استثنایی رخ دهد، در لاگ‌های خطای سیستم شما تنها خطاهای مرتبط با خود iTextSharp را مشاهده خواهید کرد و نه مشکل اصلی را که در کدهای ما وجود داشته است. البته این یک مشکل عمومی است و اگر «using statement and suppressed exceptions» را در گوگل جستجو کنید به نتایج مشابه زیادی خواهید رسید.
و خلاصه نتایج هم این است:
اگر به ثبت جزئیات خطاهای سیستم اهمیت می‌دهید (یکی از مهم‌ترین مزیت‌های دات نت نسبت به بسیاری از فریم ورک‌های مشابه که حداکثر خطای 0xABC12EF را نمایش می‌دهند)، از using استفاده نکنید! using در پشت صحنه به try/finally ترجمه می‌شود و بهتر است این مورد را دستی نوشت تا اینکه کامپایلر اینکار را به صورت خودکار انجام دهد.
در اینجا باز هم به یک سری کد تکراری try/finally خواهیم رسید و همانطور که در مباحث کاربردهای Action و Func در این سایت ذکر شد، می‌توان آن‌را تبدیل به کدهایی با قابلیت استفاده مجدد کرد. یک نمونه از پیاده سازی آن‌را در این سایت «C# Using Blocks can Swallow Exceptions » می‌توانید مشاهده کنید که خلاصه آن کلاس زیر است:
using System;

namespace Guard
{
    public static class SafeUsing
    {
        public static void SafeUsingBlock<TDisposable>(this TDisposable disposable, Action<TDisposable> action)
            where TDisposable : IDisposable
        {
            disposable.SafeUsingBlock(action, d => d);
        }

        internal static void SafeUsingBlock<TDisposable, T>(this TDisposable disposable, Action<T> action, Func<TDisposable, T> unwrapper)
            where TDisposable : IDisposable
        {
            try
            {
                action(unwrapper(disposable));
            }
            catch (Exception actionException)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception disposeException)
                {
                    throw new AggregateException(actionException, disposeException);
                }

                throw;
            }

            disposable.Dispose();
        }
    }
}
برای استفاده از کلاس فوق مثلا در حالت بکارگیری iTextSharp خواهیم داشت:
new Document(PageSize.A4).SafeUsingBlock(pdfDoc =>
{
  //todo: ...
});
علاوه بر اینکه SafeUsingBlock یک سری از اعمال تکراری را کپسوله می‌کند، از AggregateException نیز استفاده کرده است (معرفی شده در دات نت 4). به این صورت چندین استثنای رخ داده نیز در سطحی بالاتر قابل دریافت و بررسی خواهند بود و استثنایی در این بین از دست نخواهد رفت.
مطالب
تبدیل تعدادی تصویر به یک فایل PDF

صورت مساله:
تعدادی تصویر داریم، می‌خواهیم این‌ها را تبدیل به فایل PDF کنیم به این شرط که هر تصویر در یک صفحه مجزا قرار داده شود.
در ادامه برای این منظور از کتابخانه‌ی iTextSharp استفاده خواهیم کرد.

iTextSharp چیست؟
iTextSharp کتابخانه‌ی سورس باز و معروفی جهت تولید فایل‌های PDF ، توسط برنامه‌های مبتنی بر دات نت است. آن را از آدرس زیر می‌توان دریافت کرد:


کتابخانه iTextSharp نیز جزو کتابخانه‌هایی است که از جاوا به دات نت تبدیل شده‌اند. نام کتابخانه اصلی iText است و اگر کمی جستجو کنید می‌توانید کتاب 617 صفحه‌ای iText in Action از انتشارات MANNING را در این مورد نیز بیابید. هر چند این کتاب برای برنامه نویس‌های جاوا نوشته شده اما نام کلاس‌ها و متدها در iTextSharp تفاوتی با iText اصلی ندارند و مطالب آن برای برنامه نویس‌‌های دات نت هم قابل استفاده است.

مجوز استفاده از iTextSharp کدام است؟
مجوز این کتابخانه GNU Affero General Public License است. به این معنا که شما موظفید، تغییری در قسمت تهیه کننده خواص فایل PDF تولیدی که به صورت خودکار به نام کتابخانه تنظیم می‌شود، ندهید. اگر می‌خواهید این قسمت را تغییر دهید باید هزینه کنید. همچنین با توجه به اینکه این مجوز، GPL است یعنی زمانیکه از آن استفاده کردید باید کار خود را به صورت سورس باز ارائه دهید؛ درست خوندید! بله! مثل مجوز استفاده از نگارش عمومی و رایگان MySQL و اگر نمی‌خواهید اینکار را انجام دهید، در اینجا تاکید شده که باید کتابخانه را خریداری کنید.

نحوه استفاده از کتابخانه iTextSharp
در ابتدا کد تبدیل تصاویر به فایل PDF را در ذیل مشاهده خواهید کرد. فرض بر این است که ارجاعی را به اسمبلی itextsharp.dll اضافه کرده‌اید:
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace iTextSharpTests
{
public class ImageToPdf
{
public iTextSharp.text.Rectangle PdfPageSize { set; get; }
public ImageFormat ImageCompressionFormat { set; get; }
public bool FitImagesToPage { set; get; }

public void ExportToPdf(IList<string> imageFilesPath, string outPdfPath)
{
using (var pdfDoc = new Document(PdfPageSize))
{
PdfWriter.GetInstance(pdfDoc, new FileStream(outPdfPath, FileMode.Create));
pdfDoc.Open();

foreach (var file in imageFilesPath)
{
var pngImg = iTextSharp.text.Image.GetInstance(file);

if (FitImagesToPage)
{
pngImg.ScaleAbsolute(pdfDoc.PageSize.Width, pdfDoc.PageSize.Height);
}
pngImg.SetAbsolutePosition(0, 0);

//add to page
pdfDoc.Add(pngImg);
//start a new page
pdfDoc.NewPage();
}
}
}
}
}
توضیحات:
استفاده از کتابخانه‌ی iTextSharp همیشه شامل 5 مرحله است. ابتدا شیء Document ایجاد می‌شود. سپس وهله‌ای از PdfWriter ساخته شده و Document جهت نوشتن در آن گشوده خواهد شد. در طی یک سری مرحله محتویات مورد نظر به Document اضافه شده و نهایتا این شیء بسته خواهد شد. البته در اینجا چون کلاس Document اینترفیس IDisposable را پیاده سازی کرده، بهترین روش استفاده از آن بکارگیری واژه کلیدی using جهت مدیریت منابع آن است. به این ترتیب کامپایلر به صورت خودکار قطعه try/finally مرتبط را جهت پاکسازی منابع، تشکیل خواهد داد.
اندازه صفحات توسط سازنده‌ی شیء Document مشخص خواهند شد. این شیء از نوع iTextSharp.text.Rectangle است؛ اما مقدار دهی آن توسط کلاس iTextSharp.text.PageSize صورت می‌گیرد که انواع و اقسام اندازه صفحات استاندارد در آن تعریف شده‌اند.
متد iTextSharp.text.Image.GetInstance که در این مثال جهت دریافت اطلاعات تصاویر مورد استفاده قرار گرفت، 15 overload دارد که از آدرس مستقیم یک فایل تا استریم مربوطه تا Uri یک آدرس وب را نیز می‌پذیرد و از این لحاظ بسیار غنی است.

مثالی در مورد نحوه استفاده از کلاس فوق:
using System.Collections.Generic;
using System.Drawing.Imaging;

namespace iTextSharpTests
{
class Program
{
static void Main(string[] args)
{
new ImageToPdf
{
FitImagesToPage = true,
ImageCompressionFormat = ImageFormat.Jpeg,
PdfPageSize = iTextSharp.text.PageSize.A4
}.ExportToPdf(
imageFilesPath: new List<string>
{
@"D:\3.jpg",
@"D:\4.jpg"
},
outPdfPath: @"D:\tst.pdf"
);
}
}
}

مطالب
یکسان سازی ی و ک دریافتی حین استفاده از NHibernate


تصویر فوق، یکی از تصویرهایی است که شاید از طریق ایمیل‌هایی تحت عنوان "فقط در ایران!" به دست شما هم رسیده باشد. تصور کاربر نهایی (که این ایمیل را با تعجب ارسال کرده) این است که در اینجا به او گفته شده مثلا "مرتضی" را جستجو نکنید و امثال آن. چون برای او تفاوتی بین ی و ی وجود ندارد. همچنین بکار بردن "اقلامی" هم کمی غلط انداز است و بیشتر ذهن را به سمت کلمه سوق می‌دهد تا حرف.

در ادامه‌ی بحث آلرژی مزمن به وجود انواع "ی" و "ک" در بانک اطلاعاتی (+ و + و +)، اینبار قصد داریم این اطلاعات را به NHibernate بسط دهیم. شاید یک روش اعمال یک دست سازی "ی" و "ک" این باشد که در کل برنامه هر جایی که قرار است update یا insert ایی صورت گیرد، خواص رشته‌ای را یافته و تغییر دهیم. این روش "کار می‌کنه" ولی ایده آل نیست؛ چون حجم کار تکراری در برنامه زیاد خواهد شد و نگهداری آن هم مشکل می‌شود. همچنین امکان فراموش کردن اعمال آن هم وجود دارد.
در NHibernate یک سری EventListener وجود دارند که کارشان گوش فرا دادن به یک سری رخدادها مانند مثلا update یا insert است. این رخدادها می‌توانند پیش یا پس از هرگونه ثبت یا ویرایشی در برنامه صادر شوند. بنابراین بهترین جایی که جهت اعمال این نوع ممیزی (Auditing) بدون بالا بردن حجم برنامه یا اضافه کردن بیش از حد یک سری کد تکراری در حین کار با NHibernate می‌توان یافت، روال‌های مدیریت کننده‌ی همین EventListener ها هستند.

کلاس YeKeAuditorEventListener نهایی با پیاده سازی IPreInsertEventListener و IPreUpdateEventListenerبه شکل زیر خواهد بود:
using NHibernate.Event;

namespace NHYeKeAuditor
{
public class YeKeAuditorEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
// Represents a pre-insert event, which occurs just prior to performing the
// insert of an entity into the database.
public bool OnPreInsert(PreInsertEvent preInsertEvent)
{
var entity = preInsertEvent.Entity;
CorrectYeKe.ApplyCorrectYeKe(entity);
return false;
}

// Represents a pre-update event, which occurs just prior to performing the
// update of an entity in the database.
public bool OnPreUpdate(PreUpdateEvent preUpdateEvent)
{
var entity = preUpdateEvent.Entity;
CorrectYeKe.ApplyCorrectYeKe(entity);
return false;
}
}
}
در کدهای فوق روال‌های OnPreInsert و OnPreUpdate پیش از ثبت و ویرایش اطلاعات فراخوانی می‌شوند (همواره و بدون نیاز به نگرانی از فراموش شدن فراخوانی کدهای مربوطه). اینجا است که فرصت داریم تا تغییرات مورد نظر خود را جهت یکسان سازی "ی" و "ک" دریافتی اعمال کنیم (کد کلاس CorrectYeKe را در پیوست خواهید یافت).

تا اینجا فقط تعریف YeKeAuditorEventListener انجام شده است. اما NHibernate چگونه از وجود آن مطلع خواهد شد؟
برای تزریق کلاس YeKeAuditorEventListener به تنظیمات برنامه باید به شکل زیر عمل کرد:
using System;
using System.Linq;
using FluentNHibernate.Cfg;
using NHibernate.Cfg;

namespace NHYeKeAuditor
{
public static class MappingsConfiguration
{
public static FluentConfiguration InjectYeKeAuditorEventListener(this FluentConfiguration fc)
{
return fc.ExposeConfiguration(configListeners());
}

private static Action<Configuration> configListeners()
{
return
c =>
{
var listener = new YeKeAuditorEventListener();
c.EventListeners.PreInsertEventListeners =
c.EventListeners.PreInsertEventListeners
.Concat(new[] { listener })
.ToArray();
c.EventListeners.PreUpdateEventListeners =
c.EventListeners.PreUpdateEventListeners
.Concat(new[] { listener })
.ToArray();
};
}
}
}
به این معنا که FluentConfiguration خود را همانند قبل ایجاد کنید. درست در زمان پایان کار تنها کافی است متد InjectYeKeAuditorEventListener فوق بر روی آن اعمال گردد و بس (یعنی پیش از فراخوانی BuildSessionFactory).

کدهای NHYeKeAuditor را از اینجا می‌توانید دریافت کنید.

مطالب
مفاهیم برنامه نویسی ـ مروری بر پروپرتی‌ها
در مطلب پیشین کلاسی را برای حل بخشی از یک مسئله بزرگ تهیه کردیم. اگر فراموش کردید پیشنهاد می‌کنم یک بار دیگر آن مطلب را مطالعه کنید. بد نیست بار دیگر نگاهی به آن بیاندازیم.
public class Rectangle
{
    public double Width;
    public double Height;
 
    public double Area()
    {
        return Width*Height;
    }
 
    public double Perimeter()
    {
        return 2*(Width + Height);
    }
}
کلاس خوبی است اما همان طور که در بخش قبل مطرح شد این کلاس می‌تواند بهتر هم باشد. در این کلاس برای نگهداری حالت اشیائی که از روی آن ایجاد خواهند شد از متغیرهایی با سطح دسترسی عمومی استفاده شده است. این متغیرهای عمومی فیلد نامیده می‌شوند. مشکل این است که با این تعریف، اشیاء نمی‌توانند هیچ اعتراضی به مقادیر غیر معتبری که ممکن است به آن‌ها اختصاص داده شود، داشته باشند. به عبارت دیگر هیچ کنترلی بر روی مقادیر فیلدها وجود ندارد. مثلاً ممکن است یک مقدار منفی به فیلد طول اختصاص یابد. حال آنکه طول منفی معنایی ندارد.

تذکر: ممکن است این سوال پیش بیاید که خوب ما کلاس را نوشته ایم و خودمان می‌دانیم چه مقادیری برای فیلدهای آن مناسب است. اما مسئله اینجاست که اولاً ممکن است کلاس تهیه شده توسط برنامه نویس دیگری مورد استفاده قرار گیرد. یا حتی پس از مدتی فراموش کنیم چه مقادیری برای کلاسی که مدتی قبل تهیه کردیم مناسب است. و از همه مهمتر این است که کلاس‌ها و اشیاء به عنوان ابزاری برای حل مسائل هستند و ممکن است مقادیری که به فیلدها اختصاص می‌یابد در زمان نوشتن برنامه مشخص نباشد و در زمان اجرای برنامه توسط کاربر یا کدهای بخش‌های دیگر برنامه تعیین گردد.
به طور کلی هر چه کنترل و نظارت بیشتری بر روی مقادیر انتسابی به اشیاء داشته باشیم برنامه بهتر کار می‌کند و کمتر دچار خطاهای مهلک و بدتر از آن خطاهای منطقی می‌گردد. بنابراین باید ساز و کار این نظارت را در کلاس تعریف نماییم.
برای کلاس‌ها یک نوع عضو دیگر هم می‌توان تعریف کرد که دارای این ساز و کار نظارتی است. این عضو Property نام دارد و یک مکانیسم انعطاف پذیر برای خواندن، نوشتن یا حتی محاسبه مقدار یک فیلد خصوصی فراهم می‌نماید.
تا اینجا باید به این نتیجه رسیده باشید که تعریف یک متغیر با سطح دسترسی عمومی در کلاس روش پسندیده و قابل توصیه ای نیست. بنابراین متغیرها را در سطح کلاس به صورت خصوصی تعریف می‌کنیم و از طریق تعریف Property امکان استفاده از آن‌ها در بیرون کلاس را ایجاد می‌کنیم.
حال به چگونگی تعریف Property‌‌ها دقت کنید.
public class Rectangle
{
    private double _width = 0;
    private double _height = 0;
 
    public double Width
    {
        get { return _width; }
        set { if (value > 0) { _width = value; } }
    }
 
    public double Height
    {
        get { return _height; }
        set { if (value > 0) { _height = value; } }
    }
 
    public double Area()
    {
        return _width * _height;
    }
 
    public double Perimeter()
    {
        return 2*(_width + _height);
    }
}
به تغییرات ایجاد شده در تعریف کلاس دقت کنید. ابتدا سطح دسترسی دو متغیر خصوصی شده است یعنی فقط اعضای داخل کلاس به آن دسترسی دارند و از بیرون قابل استفاده نیست. نام متغیرهای پیش گفته بر اساس اصول صحیح نامگذاری فیلدهای خصوصی تغییر داده شده است. ببینید اگر اصول نامگذاری را رعایت کنید چقدر زیباست. هر جای برنامه که چشمتان به width_ بخورد فوراً متوجه می‌شوید یک فیلد خصوصی است و نیازی نیست به محل تعریف آن مراجعه کنید. از طرفی یک مقدار پیش فرض برای این دو فیلد در نظر گرفته شده است که در صورتی که مقدار مناسبی برای آن‌ها تعیین نشد مورد استفاده قرار خواهند گرفت.
دو قسمت اضافه شده دیگر تعریف دو Property مورد نظر است. یکی عرض و دیگری ارتفاع. خط اول تعریف پروپرتی تفاوتی با تعریف فیلد عمومی ندارد. اما همان طور که می‌بینید هر فیلد دارای یک بدنه است که با {} مشخص می‌شود. در این بدنه ساز و کار نظارتی تعریف می‌شود.
نحوه دسترسی به پروپرتی‌ها مشابه فیلدهای عمومی است. اما پروپرتی‌ها در حقیقت متدهای ویژه ای به نام اکسسور (Accessor) هستند که از طرفی سادگی استفاده از متغیرها را به ارمغان می‌آورند و از طرف دیگر دربردارنده امنیت و انعطاف پذیری متدها هستند. یعنی در عین حال که روشی عمومی برای داد و ستد مقادیر ارایه می‌دهند، کد پیاده سازی یا وارسی اطلاعات را مخفی نموده و استفاده کننده کلاس را با آن درگیر نمی‌کنند. قطعه کد زیر چگونگی استفاده از پروپرتی را نشان می‌دهد.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10;
Console.WriteLine(rectangle.Width);
به خوبی مشخص است برای کد استفاده کننده از شیء که آن‌را کد مشتری می‌نامیم نحوه دسترسی به پروپرتی یا فیلد تفاورتی ندارد. در اینجا خط دوم که مقداری را به یک پروپرتی منتسب کرده سبب فراخوانی اکسسور set می‌گردد. همچنین مقدار منتسب شده یعنی ۱۰ در داخل بدنه اکسسور از طریق کلمه کلیدی value قابل دسترسی و ارزیابی است. در خط سوم که لازم است مقدار پروپرتی برای چاپ بازیابی یا خوانده شود منجر به فراخوانی اکسسور get می‌گردد.
تذکر: به دو اکسسور get و set مانند دو متد معمولی نگاه کنید از این نظر که می‌توانید در بدنه آن‌ها اعمال دلخواه دیگری بجز ذخیره و بازیابی اطلاعات پروپرتی را نیز انجام دهید.

چند نکته:
  • اکسسور get هنگام بازگشت یا خواندن مقدار پروپرتی اجرا می‌شود و اکسسور set زمان انتساب یک مقدار جدید به پروپرتی فراخوانی می‌شود. جالب آنکه در صورت لزوم این دو اکسسور می‌توانند دارای سطوح دسترسی متفاوتی باشند.
  • داخل اکسسور set کلمه کلیدی value مقدار منتسب شده را در اختیار قرار می‌دهد تا در صورت لزوم بتوان بر روی آن پردازش لازم را انجام داد.
  • یک پروپرتی می‌تواند فاقد اکسسور set باشد که در این صورت یک پروپرتی فقط خواندنی ایجاد می‌گردد. همچنین می‌تواند فقط شامل اکسسور set باشد که در این صورت فقط امکان انتساب مقادر به آن وجود دارد و امکان دریافت یا خواندن مقدار آن میسر نیست. چنین پروپرتی ای فقط نوشتنی خواهد بود.
  • در بدنه اکسسور set الزامی به انتساب مقدار منتسب توسط کد مشتری نیست. در صورت صلاحدید می‌توانید به جای آن هر مقدار دیگری را در نظر بگیرید یا عملیات مورد نظر خود را انجام دهید.
  • در بدنه اکسسور get هم هر مقداری را می‌توانید بازگشت دهید. یعنی الزامی وجود ندارد حتماً مقدار فیلد خصوصی متناظر با پروپرتی را بازگشت دهید. حتی الزامی به تعریف فیلد خصوصی برای هر پروپرتی ندارید. به طور مثال ممکن است مقدار بازگشتی اکسسور get حاصل محاسبه و ... باشد.

اکنون مثال دیگری را در نظر بگیرید. فرض کنید در یک پروژه فروشگاهی در حال تهیه کلاسی برای مدیریت محصولات هستید. قصد داریم یک پروپرتی ایجاد کنیم تا نام محصول را نگهداری کند و در حال حاضر هیچ محدودیتی برای نام یک محصول در نظر نداریم. کد زیر را ببینید.
public class Product
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}
همانطور که می‌بینید در بدنه اکسسورهای پروپرتی Name هیچ عملیات نظارتی ای در نظر گرفته نشده است. آیا بهتر نبود بیهوده پروپرتی تعریف نکنیم و خیلی ساده از یک فیلد عمومی که همین کار را انجام می‌دهد استفاده کنیم؟ خیر. بهتر نبود. مهمترین دلیلی که فعلاً کافی است تا شما را قانع کند تعریف پروپرتی روش پسندیده‌تری از فیلد عمومی است را بررسی می‌کنیم.
فرض کنید پس از مدتی متوجه شدید اگر نام بسیار طولانی ای برای محصول درج شود ظاهر برنامه شما دچار مشکل می‌شود. پس باید بر روی این مورد نظارت داشته باشید. دیدیم که برای رسیدن به این هدف باید فیلد عمومی را فراموش و به جای آن پروپرتی تعریف کنیم. اما مسئله اینست که تبدیل یک فیلد عمومی به پروپرتی میتواند سبب بروز ناسازگاری‌هایی در بخش‌های دیگر برنامه که از این کلاس و آن فیلد استفاده می‌کنند شود. پس بهتر آن است که از ابتدا پروپرتی تعریف کنیم هر چند نیازی به عملیات نظارتی خاصی نداریم. در این حالت اگر نیاز به پردازش بیشتر پیدا شد به راحتی می‌توانیم کد مورد نظر را در اکسسورهای موجود اضافه کنیم بدون آنکه نیازی به تغییر بخش‌های دیگر باشد.
و یک خبر خوب! از سی شارپ ۳ به بعد ویژگی جدیدی در اختیار ما قرار گرفته است که می‌توان پروپرتی‌هایی مانند مثال بالا را که نیازی به عملیات نظارتی ندارند، ساده‌تر و خواناتر تعریف نمود. این ویژگی جدید پروپرتی اتوماتیک یا Auto-Implemented Property نام دارد. مانند نمونه زیر.
public class Product
{
    public string Name { get; set; }
}
این کد مشابه کد پیشین است با این تفاوت که خود کامپایلر یک متغیر خصوصی و بی نام را ایجاد می‌نماید که فقط داخل اکسسورهای پروپرتی قابل دسترسی است.
البته استفاده از پروپرتی برتری دیگری هم دارد. و آن کنترل سطح دسترسی اکسسورها است.  مثال زیر را ببینید.
public class Student
{
    public DateTime Birthdate { get; set; }
 
    public double Age { get; private set; }
}
کلاس دانشجو یک پروپرتی به نام تاریخ تولد دارد که قابل خواندن و نوشتن توسط کد مشتری (کد استفاده کننده از کلاس یا اشیاء آن) است. و یک پروپرتی دیگر به نام سن دارد که توسط کد مشتری تنها قابل خواندن است. و تنها توسط سایر اعضای داخل همین کلاس قابل نوشتن است. چون اکسسور set آن به صورت خصوصی تعریف شده است. به این ترتیب بخش دیگری از کلاس سن دانشجو را بر اساس تاریخ تولد او محسابه می‌کند و در پروپرتی Age قرار می‌دهد و کد مشتری می‌تواند آن‌را مورد استفاده قرار دهد اما حق دستکاری آن‌را ندارد. به همین ترتیب در صورت نیاز اکسسور get را می‌توان خصوصی کرد تا پروپرتی از دید کد مشتری فقط نوشتنی باشد. اما حتماً می‌توانید حدس بزنید که نمی‌توان هر دو اکسسور را خصوصی کرد. چرا؟
تذکر: در هنگام تعریف یک فیلد می‌توان از کلمه کلیدی readonly استفاده کرد تا یک فیلد فقط خواندنی ایجاد گردد. اما در اینصورت فیلد تعریف شده حتی داخل کلاس هم فقط خواندنی است و فقط در هنگام تعریف یا در متد سازنده کلاس امکان مقدار دهی به آن وجود دارد. در بخش‌های بعدی مفهوم سازنده کلاس مورد بررسی خواهد گرفت.
نظرات مطالب
استفاده از قابلیت پارتیشن بندی در آرشیو جداول بانک‌های اطلاعاتی SQL Server
با سلام؛ من از روش فوق با جدولی حدود 4 میلیون رکورد با فیلد تاریخ از نوع varchar(10 ) استفاده کردم. زمانیکه جستجو میزنم در یک بازه زمانی فایل گروه، در یک جدول پارتیشن بندی و همون کوئری با جدول بدون پارتیش بندی میزنم هیچ تفاوتی نمی‌کند. فرمت تاریخ در دیتا بیس بصورت n'1396-05-11'  می‌باشد و هر فایل گروپ روی دیسک مجزا نمی‌باشد و فیلد تاریخ  به عنوان NonClusteredIndex  تعریف شده. دلیل خاصی دارد که من نمی‌تونم نتیجه‌ای بگیرم؟ با سپاس
select  * from Inbox where  SendDate>'1395-12-30' and  SendDate<'1396-12-31'

مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت هشتم - تنظیمات پروژه‌های React برای کار با Mobx decorators
تا اینجا دو مثالی را که از Mobx بررسی کردیم (مثال ورود متن و مثال کامپوننت شمارشگر)، به عمد به همراه decoratorهای @ دار آن نبودند. برای مثال در قسمت قبل، یک کلاس را با یک خاصیت ایجاد کردیم که روش مزین سازی خاصیت value آن را با  observable decorator، توسط متد decorate انجام دادیم و این هم یک روش کار با MobX است؛ بدون اینکه نیاز به تنظیمات خاصی را داشته باشد:
import { decorate } from "mobx";

class Count {
  value = 0;
}

decorate(Count, { value: observable });
const count = new Count();
اما اگر همان مثال بسیار ساده‌ی ورود متن را بخواهیم توسط decoratorهای @ دار MobX پیاده سازی کنیم ... پروژه‌ی استاندارد React ما کامپایل نخواهد شد که در این قسمت، روش رفع این مشکل را بررسی می‌کنیم.


بازنویسی مثال ورود متن و نمایش آن با Mobx decorators

در اینجا یک text-box، به همراه دو div در صفحه رندر خواهند شد که قرار است با ورود اطلاعاتی در text-box، یکی از آن‌ها (text-display) این اطلاعات را به صورت معمولی و دیگری (text-display-uppercase) آن‌را به صورت uppercase نمایش دهد. روش کار انجام شده هم مستقل از React است و به صورت مستقیم با استفاده از DOM API عمل شده‌است. این مثال را پیشتر در اولین قسمت بررسی MobX، ملاحظه کردید. اکنون اگر بخواهیم بجای شیءای که توسط متد observable کتابخانه‌ی MobX محصور شده‌است:
const text = observable({
  value: "Hello world!",
  get uppercase() {
    return this.value.toUpperCase();
  }
});
از یک کلاس ES6 به همراه Mobx decorators استفاده کنیم، به یک چنین پروژه‌ی جدیدی خواهیم رسید:
ابتدا یک پروژه‌ی جدید React را ایجاد می‌کنیم:
> create-react-app state-management-with-mobx-part3
> cd state-management-with-mobx-part3
> npm start
در ادامه کتابخانه‌ی mobx را نیز نصب می‌کنیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های ctrl+` را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save mobx
در ادامه، ابتدا فایل public\index.html را جهت نمایش دو div و یک text-box، ویرایش می‌کنیم:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>MobX Basics, part 3</title>
    <meta charset="UTF-8" />
    <link href="src/styles.css" />
  </head>

  <body>
    <main>
      <input id="text-input" />
      <p id="text-display"></p>
      <p id="text-display-uppercase"></p>
    </main>

    <script src="src/index.js"></script>
  </body>
</html>
سپس محتویات فایل src\index.js را نیز به نحو زیر تغییر می‌دهیم:
import { autorun, computed, observable } from "mobx";

const input = document.getElementById("text-input");
const textDisplay = document.getElementById("text-display");
const loudDisplay = document.getElementById("text-display-uppercase");

class Text {
  @observable value = "Hello World";
  @computed get uppercase() {
    return this.value.toUpperCase();
  }
}

const text = new Text();

input.addEventListener("keyup", event => {
  text.value = event.target.value;
});

autorun(() => {
  input.value = text.value;
  textDisplay.textContent = text.value;
  loudDisplay.textContent = text.uppercase;
});
تنها تفاوت این نگارش با نگارش قبلی آن، استفاده از کلاس Text که یک کلاس ES6 به همراه MobX Decorators است، بجای یک شیء ساده‌ی جاوا اسکریپتی می‌باشد. در اینجا خاصیت value به صورت observable@ تعریف شده و در نتیجه‌ی تغییر مقدار آن در کدهای برنامه، خاصیت محاسباتی وابسته‌ی به آن یا همان uppercase که با computed@ تزئین شده، به صورت خودکار به روز رسانی خواهد شد. متد autorun نیز به این تغییرات که حاصل فشرده شدن کلیدها هستند، واکنش نشان داده و متن دو div موجود در صفحه را به روز رسانی می‌کند.

اکنون اگر در همین حال، برنامه را با دستور npm start اجرا کنیم، با خطای زیر متوقف خواهیم شد:
./src/index.js
SyntaxError: \src\index.js: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (8:3):

   6 | 
   7 | class Text {
>  8 |   @observable value = "Hello World";
     |   ^
   9 |   @computed get uppercase() {
  10 |     return this.value.toUpperCase();
  11 |   }

راه حل اول: از Decorators استفاده نکنیم!

یک راه حل مشکل فوق این است که بدون هیچ تغییری در ساختار پروژه‌ی React خود، اصلا از decorator syntax استفاده نکنیم. برای مثال اگر یک کلاس متداول MobX ای چنین شکلی را دارد:
import { observable, computed, action } from "mobx";

class Timer {
  @observable start = Date.now();
  @observable current = Date.now();

  @computed
  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  @action
  tick() {
    this.current = Date.now();
  }
}
می‌توان آن‌را بدون استفاده از decorator syntax، به صورت زیر نیز تعریف کرد:
import { observable, computed, action, decorate } from "mobx";

class Timer {
  start = Date.now();
  current = Date.now();

  get elapsedTime() {
    return this.current - this.start + "milliseconds";
  }

  tick() {
    this.current = Date.now();
  }
}

decorate(Timer, {
  start: observable,
  current: observable,
  elapsedTime: computed,
  tick: action
});
نمونه‌ی این روش را در قسمت قبل با تعریف شیء شمارشگر مشاهده کرده‌اید. در اینجا با توجه به اینکه Decorators در جاوا اسکریپت چیزی نیستند بجز بیان زیبای higher-order functions و higher-order functions هم توابعی هستند که توابع دیگر را با ارائه‌ی قابلیت‌های بیشتری، محصور می‌کنند، به همین جهت هر کاری را که بتوان با تزئین کننده‌ها انجام داد، همان را با توابع معمولی جاوا اسکریپتی نیز می‌توان انجام داد. اینکار را در مثال فوق توسط متد decorate مشاهده می‌کنید. این متد ابتدا نوع کلاس خاصی را دریافت کرده و سپس در پارامتر دوم آن می‌توان شیءای را تعریف کرد که خواص آن، همان خواص کلاس پارامتر اول است و مقادیر این خواص، تزئین کننده‌هایی هستند که قرار است برای آن‌ها بکار گرفته شوند. مزیت این روش بدون تغییر باقی ماندن تعریف کلاس Timer در اینجا و همچنین انجام هیچگونه تغییری در ساختار پروژه‌ی React، بدون نیاز به نصب بسته‌های کمکی اضافی است.

همچنین در این حالت بجای استفاده از کامپوننت‌های کلاسی، باید از روش بکارگیری متد observer برای محصور کردن کامپوننت تابعی تعریف شده استفاده کرد (تا دیگر نیازی به ذکر observer class@ نباشد):
const Counter = observer(({ count }) => {
  return (
   // ...
  );
});


راه حل دوم: از تایپ‌اسکریپت استفاده کنید!

create-react-app امکان ایجاد پروژه‌های React تایپ‌اسکریپتی را با ذکر سوئیچ typescript نیز دارد:
> create-react-app my-proj1 --typescript
پس از ایجاد پروژه، فایل tsconfig.json آن‌را یافته و experimentalDecorators آن‌را به true تنظیم کنید:
{
  "compilerOptions": {
    // ...
    "experimentalDecorators": true
  }
}
این تنها تغییری است که مورد نیاز می‌باشد و پس از آن برنامه‌ی React جاری، بدون مشکلی می‌تواند با decorators کار کند.


فعالسازی MobX Decorators در پروژه‌های استاندارد React مبتنی بر ES6

MobX از legacy" decorators spec" پشتیبانی می‌کند. یعنی اگر پروژه‌ای از spec جدید استفاده کند، دیگر نخواهد توانست با MobX فعلی کار کند. این هم مشکل MobX نیست. مشکل اینجا است که باید دانست کلا decorators در زبان جاوااسکریپت هنوز در مرحله‌ی آزمایشی قرار دارند و تکلیف spec نهایی و تائید شده‌ی آن مشخص نیست.
برای فعالسازی decorators در یک پروژه‌ی React استاندارد مبتنی بر ES6، شاید کمی جستجو کنید و به نتایجی مانند افزودن فایل babelrc. به ریشه‌ی پروژه و نصب افزونه‌هایی مانند babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties@ برسید. اما ... این‌ها بدون اجرای دستور npm run eject کار نمی‌کنند و اگر این دستور را اجرا کنیم، در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر می‌شوند). همچنین این عملیات نیز یک طرفه‌است. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانی‌های بعدی create-react-app را با مشکل مواجه می‌کند. این گزینه صرفا مختص توسعه دهندگان پیشرفته‌ی React است. به همین جهت نیاز به روشی را داریم تا بتوانیم تنظیمات Webpack و کامپایلر Babel را بدون اجرای دستور npm run eject، تغییر دهیم تا در نتیجه، decorators را در آن فعال کنیم و خوشبختانه پروژه‌ی react-app-rewired دقیقا برای همین منظور طراحی شده‌است.

بنابراین ابتدا بسته‌های زیر را نصب می‌کنیم:
> npm i --save-dev customize-cra react-app-rewired
بسته‌ی react-app-rewired، امکان بازنویسی تنظیمات webpack پروژه‌ی react را بدون eject آن میسر می‌کند. customize-cra نیز با استفاده از امکانات همین بسته، نگارش‌های جدیدتر create-react-app را پشتیبانی می‌کند.

پس از نصب این پیشنیازها، فایل جدید config-overrides.js را به ریشه‌ی پروژه، جائیکه فایل package.json قرار گرفته‌است، با محتوای زیر اضافه کنید تا پشتیبانی ازlegacy" decorators spec" فعال شوند:
const {
  override,
  addDecoratorsLegacy,
  disableEsLint
} = require("customize-cra");

module.exports = override(
  // enable legacy decorators babel plugin
  addDecoratorsLegacy(),

  // disable eslint in webpack
  disableEsLint()
);
در ادامه فایل package.json را گشوده و قسمت scripts آن‌را برای استفاده‌ی از react-app-rewired، به صورت زیر بازنویسی کنید تا امکان تغییر تنظیمات webpack به صورت پویا در زمان اجرای برنامه، میسر شود:
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },
پس از این تغییرات، نیاز است دستور npm start را یکبار دیگر از ابتدا اجرا کنید. اکنون برنامه بدون مشکل کامپایل شده و خروجی بدون خطایی در مرورگر نمایش داده خواهد شد.


تنظیمات ESLint مخصوص کار با decorators

فایل ویژه‌ی eslintrc.json. که در ریشه‌ی پروژه قرار می‌گیرد (این فایل بدون نام است و فقط از پسوند تشکیل شده)، برای پروژه‌های MobX، باید حداقل تنظیم زیر را داشته باشد تا ESLint بتواند legacyDecorators را نیز پردازش کند:
{
  "extends": "react-app",
  "parserOptions": {
    "ecmaFeatures": {
      "legacyDecorators": true
    }
  }
}
و یا یک نمونه‌ی غنی شده‌ی فایل eslintrc.json. مخصوص برنامه‌های React به صورت زیر است:
{
    "env": {
        "node": true,
        "commonjs": true,
        "browser": true,
        "es6": true,
        "mocha": true
    },
    "settings": {
        "react": {
            "version": "detect"
        }
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true,
            "legacyDecorators": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "plugins": [
        "babel",
        "react",
        "react-hooks",
        "react-redux",
        "no-async-without-await",
        "css-modules",
        "filenames",
        "simple-import-sort"
    ],
    "rules": {
        "no-const-assign": "warn",
        "no-this-before-super": "warn",
        "constructor-super": "warn",
        "strict": [
            "error",
            "safe"
        ],
        "no-debugger": "error",
        "brace-style": [
            "error",
            "1tbs",
            {
                "allowSingleLine": true
            }
        ],
        "no-trailing-spaces": "error",
        "keyword-spacing": "error",
        "space-before-function-paren": [
            "error",
            "never"
        ],
        "spaced-comment": [
            "error",
            "always"
        ],
        "vars-on-top": "error",
        "no-undef": "error",
        "no-undefined": "warn",
        "comma-dangle": [
            "error",
            "never"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ],
        "guard-for-in": "error",
        "no-eval": "error",
        "no-with": "error",
        "valid-typeof": "error",
        "no-unused-vars": "error",
        "no-continue": "warn",
        "no-extra-semi": "warn",
        "no-unreachable": "warn",
        "no-unused-expressions": "warn",
        "max-len": [
            "warn",
            80,
            4
        ],
        "react/prefer-es6-class": "warn",
        "react/jsx-boolean-value": "warn",
        "react-hooks/rules-of-hooks": "error",
        "react-hooks/exhaustive-deps": "warn",
        "react/prop-types": "off",
        "react-redux/mapDispatchToProps-returns-object": "off",
        "react-redux/prefer-separate-component-file": "off",
        "no-async-without-await/no-async-without-await": "warn",
        "css-modules/no-undef-class": "off",
        "filenames/match-regex": [
            "off",
            "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$",
            true
        ],
        "filenames/match-exported": "off",
        "filenames/no-index": "off",
        "simple-import-sort/sort": "error"
    },
    "extends": [
        "react-app",
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:react-redux/recommended",
        "plugin:css-modules/recommended"
    ],
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly",
        "process": true
    }
}
البته برای اینکه این تنظیمات کار کند، باید افزونه‌های زیر را نیز به صورت محلی در ریشه‌ی پروژه‌ی جاری نصب کنید (این مورد از ESLint 6x به بعد اجباری است و از بسته‌های global استفاده نمی‌کند):
>npm i --save-dev eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort eslint-loader typescript
پس از آن می‌توان فایل config-overrides.js را به صورت زیر نیز بر اساس تنظیمات فوق، بهبود بخشید:
const {
  override,
  addDecoratorsLegacy,
  useEslintRc
} = require("customize-cra");

module.exports = override(
  addDecoratorsLegacy(),
  useEslintRc(".eslintrc.json")
);


رفع اخطار مرتبط با decorators در VSCode

تا اینجا کار تنظیم کامپایلر babel، جهت پردازش decorators انجام شد. اما خود VSCode نیز چنین اخطاری را در پروژه‌هایی که از decorates استفاده می‌کنند، نمایش می‌دهد:
Experimental support for decorators is a feature that is subject to change in a future release.
Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.ts(1219)
برای رفع آن، فایل جدید tsconfig.json را در ریشه‌ی پروژه ایجاد کرده و آن‌را به صورت زیر تکمیل کنید تا ادیتور تایپ‌اسکریپتی VSCode، دیگر خطاهای مرتبط با decorators را نمایش ندهد:
{
    "compilerOptions": {
        "experimentalDecorators": true,
        "allowJs": true
    }
}

کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید: state-management-with-mobx-part3.zip