PersianWord
خلاصهای کوتاه در مورد WinRT
- از این جهت که رابطهای کاربری مبتنی بر WinRT ، یا بر پایه XAML است یا HTML/CSS ، یادگیری WPF و یا سیلورلایت (که قسمتی از WPF را به ارث برده) مفید خواهند بود؛ از این لحاظ که پایه رابط کاربری هر دوی اینها هم XAML است و اساسا طراحی XAML از اینجا به WinRT منتقل شده.
کلا برای برنامه نویسهای دات نت WinRT مثل یک سری اسمبلی جدید است که اضافه شده و یک سری اسمبلی از آنها گرفته شده. هیچ تفاوت دیگری از لحاظ اصول برنامه نویسی نمیکند. یک سری فضای نام جدید و کلاس جدید دارید. یک سری از کلاسهای پیشین به دلیل محدودیتهای امنیتی، دیگر در WinRT قابل استفاده نیست. مثلا همینطوری دیگه نمیتونید هر جایی فایل جدید درست کنید، یک سری آداب و اصول خاص خودش را دارد.
ضمنا این رو هم در نظر داشته باشید که WinRT یک سیستم همه منظوره نیست و ... بین خودمان باشد بیشتر در سطح دسکتاپ برای کارهای شیک و چشم نواز و برنامههای فانتزی طراحی شده. اصل کارهای برنامههای تجاری باز هم بر اساس همان سیستمهای وب و یا دسکتاپ سابق خواهد بود.
- یادگیری سی++ همیشه مفید است. حتی در کره مریخ هم تاجایی که اطلاع دارم (!) یک کامپایلر سی++ وجود دارد و میشود با آن برنامهی Hello world را کامپایل کرد. اگر باور ندارید از این لینوکسیها بپرسید!
کتابهای زیادی در مورد شیرپوینت نوشته شده، اما این یکی متفاوت است. در طی فصول مختلف این کتاب، نحوهی ایجاد یک سایت مدیریت پروژه به همراه کلیه فرمها، گردشهای کاری و گزارشات مرتبط به صورت قدم به قدم، با تصاویر و توضیحات لازم بیان شده است.
لیست فصول مختلف این کتاب به شرح زیر است :
Chapter 1: Introduction
Chapter 2: Collecting Requirements
Chapter 3: Processing Incoming E-mail
Chapter 4: Managing Requirements
Chapter 5: Supporting Discussions
Chapter 6: User Stories
Chapter 7: Project Backlog
Chapter 8: Iteration Backlog
Chapter 9: Burndown Charts
Chapter 10: Getting Organized
Chapter 11: Creating Test Cases
Chapter 12: Reporting Defects
Chapter 13: Testing Metrics
Chapter 14: Workflow Tasks
Chapter 15: State Machine Workflows
Chapter 16: Creating Custom Forms
برای نمونه هدف از فصل user stories آن رسیدن به فرمی شبیه به فرم زیر و به گردش انداختن آن بدون حتی یک سطر برنامه نویسی است:
در حاشیه!
کلا یکی از اهداف مهم شیرپوینت بیکار کردن برنامه نویسهای ASP.NET و سپردن کار آنها به business analyst ها است و مایکروسافت در این زمینه بسیار موفق عمل کرده است! (البته این را هم داخل پرانتز عرض کنم که برای راه اندازی و نگهداری شیرپوینت حتما نیاز به یک PHD از مایکروسافت خواهید داشت. اگر باور ندارید فقط یکبار چندماهی آزمایش کنید! به همین دلیل است که هنوز برنامه نویسهای ASP.NET منقرض نشدهاند!)
به نظر من jQuery به چندین دلیل از کتابخانه MS Ajax و ملحقات آن مهمتر است و باید به آن پرداخته شود:
1- دانش شما قابل انتقال است. اگر روزی به PHP یا JSP یا موارد مشابه دیگری مهاجرت کردید، دانش jQuery شما باز هم قابل استفاده خواهد بود.
2- این کتابخانه بسیار سبکتر و کم حجم تر از MS Ajax است. (حجم آن کمتر از نصف است(+))
3- تقریبا از اکثر فریم ورکهای جاوا اسکریپتی موجود سریعتر است. (+)
4- پشتیبانی آن از مرورگرهای مختلف بینظیر است. لازم نیست عمر گرانمایه را صرف این کنید که چرا این اسکریپت در فایرفاکس کار میکنه اما در IE خیر.
5- کوهی از افزونههای مختلف برای آن موجود است. (jQuery plugin را در گوگل جستجو کنید)
6- استفاده از آن، میزان کد جاوا اسکریپتی را که باید نوشت تا حد بسیار قابل ملاحظهای کاهش میدهد، که در نهایت سبب ایجاد کدی خواناتر در مدت زمانی کمتر میگردد.
7- کدنویسی با jQuery از کد نویسی JavaScript ایی خالص بسیار سادهتر است. افرادی که تا دیروز حتی طرف جاوا اسکریپت هم نمیرفتند، امروزه پلاگینهای jQuery مینویسند.
8- مستندات کاملی داشته به همراه مثالهایی بسیار کاربردی.
9- به دلیل استفاده شدن از آن در پلتفرمهای مختلف، مثالهای کاربردی بیشتری داشته، همچنین منابع آموزشی پر محتواتری را نیز میتوان در این رابطه یافت. (میتوان tutorial های مربوط به php را مشاهده و آنها را به ASP.Net تبدیل کرد و صرفا منحصر به یک عده و یک پلتفرم نیست)
10- خلاقیت شما را نکشته و از شما فردی که هیچگونه درکی نسبت به عملیات اجکسی رخ داده ندارد، نخواهد ساخت. (MS Ajax برای ASP.Net یک شاهکار است. چند عدد محصور کننده را استفاده کنید، برنامه نویسی شما با روش سابق "هیچ" تفاوتی نخواهد داشت. مسلما این توانمندی تیم مایکروسافت را نمایش میدهد، نه توانمندی من و شما را! اما اگر نیاز به کمی ابتکار وجود داشت اینجا است که گیر خواهید افتاد و موارد 6 و 7 و 9 ذکر شده، jQuery را بر این کتابخانه مقدم خواهد ساخت)
11- با آمدن ASP.Net MVC تمایل به استفاده از jQuery به شدت افزایش یافته که نمود آنرا در وبلاگهای مطرح در این زمینه میتوان مشاهده کرد (اکثر مطالب ارسالی این روزهای بلاگهای ASP.Net حول و حوش ASP.Net MVC و jQuery است).
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- تنظیم Http Proxy در CVS
- URL Rewrite Module for IIS 7.0
- تریگرها در SQL Server
- گستره سیاست ها در Group Policy
- لینوس توروالدز به سوی میزکار گنوم باز می گردد
- ابر برچسبها و مقایسهی سخنرانیهای بوش و اوباما
- اندیسهای فیلتر شده
ASP. Net
- ASP.NET MVC 1.0 Release Candidate
- ویدیوی معرفی ASP.NET MVC و ویدیویی دیگر
- GridView and CommandArguments
طراحی و توسعه وب
PHP
- PDT 2.0 : ابزارهای جدیدی برای برنامه نویسهای PHP
- NetBeans ، PHP و کار با دیتابیس
- بررسی سرعت و میزان مصرف حافظهی متدهای مختلف در PHP
سی شارپ
عمومی دات نت
ویندوز
(ایکاش بجای تمام اینکارها یک سیستم سادهتر توسعهی پلاگین برای آن طراحی میکردند ... یا به عبارتی یکی از مهمترین دلیلهای اقبال مردم به فایرفاکس را به صورت بسیار کم رنگی دارد)
مسایل اجتماعی و انسانی برنامه نویسی
- داستان استفاده از برنامههای خود پیش از استفاده دیگران
- راههایی برای افزایش بهره وری
- راههایی برای کاهش بهره وری!
متفرقه
انواع رخدادها: بومی و سفارشی
دو رده بندی عمومی رخدادها در مرورگرها وجود دارند: بومی و سفارشی.
بومیها همانهایی هستند که در مستندات رسمی استانداردهای وب ذکر شدهاند؛ مانند click که توسط ماوس و یا صفحه کلید فعال میشود و یا load که در زمان بارگذاری کامل صفحه، تصاویر و یا یک iframe رخ میدهد.
رخدادهای سفارشی مواردی هستند که توسط یک کتابخانهی خاص و یا جهت یک برنامهی خاص تهیه شدهاند. مانند یک رخداد سفارشی که زمان شروع آپلود یک فایل را اعلام میکند.
رخدادهای سفارشی که بدون jQuery ایجاد و رخمیدهند، توسط jQuery نیز قابل بررسی و مدیریت هستند و نه برعکس. به عبارتی رخدادهای سفارشی ایجاد شدهی توسط jQuery غیراستاندارد بوده و صرفا مختص به API آن هستند.
در این بین، شیء استاندارد Event کار اتصال رخدادهای سفارشی و استاندارد را انجام میدهد. هر نوع رخداد DOM (سفارشی و یا بومی)، توسط یک شیء Event بیان میشود که آن نیز به همراه تعدادی خاصیت و متد، جهت مدیریت این رخداد است. برای مثال رخداد click دارای خاصیت type ایی به نام click است که در شیء Event متناظر با آن تعریف شدهاست.
انتشار رخدادها در صفحه
در روزهای آغازین وب، Netscape روش event capturing را برای انتشار رخدادها در صفحه ارائه داد و در مقابل آن IE روش event bubbling را معرفی کرد که متضاد یکدیگر بودند. در سال 2000 با ارائه استاندارد DOM Level 2 Events Specification، این وضعیت تغییر کرد و شامل هر دو مورد event capturing و event bubbling است و در حال حاضر تمام مرورگرهای مدرن این استاندارد را پیاده سازی کردهاند. بر اساس این استاندارد، زمانیکه رویدادی خلق میشود، فاز capturing آغاز میگردد که از شیء window شروع، سپس به شیء document منتشر میشود و این روند تا رسیدن به المانی که سبب بروز رخداد شدهاست ادامه پیدا میکند. پس از پایان فاز capturing، فاز جدید bubbling شروع میشود. در این فاز، رخداد از تمام والدین شیء هدف عبور میکند تا به شیء window برسد.
برای مثال اگر سند HTML ما چنین تعریفی را داشته باشد و بر روی المان «child of child of one» کلیک شده باشد:
<!DOCTYPE html> <html> <head> <title>event propagation demo</title> </head> <body> <section> <h1>nested divs</h1> <div>one <div>child of one <div>child of child of one</div> </div> </div> </section> </body> </html>
1.window 2.document 3.<html> 4.<body> 5.<section> 6.<div>one 7.<div>child of one 8.<div>child of child of one
9.<div>child of child of one 10.<div>child of one 11.<div>one 12.<section> 13.<body> 14.<html> 15.document 16.window
البته باید درنظر داشت که jQuery از روش ارائه شدهی توسط مرورگر برای فاز Bubbling استفاده نمیکند و این مسیر را خودش مجددا محاسبه و رخدادگردانهای این مسیر را به صورت دستی اجرا میکند. به همین جهت کارآیی آن نسبت به روش توکار و بومی مرورگرها کمتر است.
ایجاد رخدادهای DOM و صدور آنها در jQuery
برای نمایش ایجاد و صدور رخدادهای DOM با و بدون jQuery، از قطعه کد HTML زیر استفاده میکنیم:
<div> <button type="button">do something</button> </div> <form method="POST" action="/user"> <label>Enter user name: <input name="user"> </label> <button type="submit">submit</button> </form>
// submits the form $('FORM').trigger('submit'); // submits the form by clicking the button $('BUTTON[type="submit"]').trigger('click'); // focuses the text input $('INPUT').trigger('focus'); // removes focus from the text input $('INPUT').trigger('blur');
هرچند روش دومی نیز در jQuery API برای انجام همینکارها نیز پیش بینی شدهاست:
// submits the form $('FORM').submit(); // submits the form by clicking the button $('BUTTON[type="submit"]').click(); // focuses the text input $('INPUT').focus(); // removes focus from the text input $('INPUT').blur();
در ادامه فرض کنید یک دکمه داخل یک div قرار گرفتهاست و آن div نیز به همراه یک مدیریت کنندهی رخداد کلیک است. در این حالت اگر بخواهیم با کلیک بر روی دکمه سبب اجرای رویدادگردان div والد نشویم، میتوان از متد triggerHandler استفاده کرد:
// clicks the first button - the click event does not bubble $('BUTTON[type="button"]').triggerHandler('click');
ایجاد رخدادهای DOM و صدور آنها در جاوا اسکریپت (بدون استفاده از jQuery)
در web API مرورگرها، برای انجام بروز رخدادهای معادل مثالی که با jQuery مطرح شد، میتوان متدهای بومی متناظر با این رخدادها را بر روی المانها فراخوانی کرد:
// submits the form document.querySelector('FORM').submit(); // submits the form by clicking the button document.querySelector('BUTTON[type="submit"]').click(); // focuses the text input document.querySelector('INPUT').focus(); // removes focus from the text input document.querySelector('INPUT').blur();
متدهای توکار و بومی click ،focus و blur بر روی تمام عناصر DOM که از اینترفیس HTMLElement مشتق شده باشند، وجود دارند. متد submit فقط بر روی المانهایی از نوع <form> وجود دارد و قابل فراخوانی است.
باید دقت داشت که فراخوانی متدهای click و submit از نوع bubbling است؛ اما متدهای focus و blur خیر. از این جهت که این دو رخداد فاز capturing را سبب میشوند.
متدهای یاد شده را توسط سازندهی شیء Event و یا متد createEvent شیء document نیز میتوان ایجاد کرد. یکی از کاربردهای آن، ارائهی رفتاری سفارشی مانند triggerHandler جیکوئری است:
var clickEvent; if (typeof Event === 'function') { clickEvent = new Event('click', {bubbles: false}); } else { clickEvent = document.createEvent('Event'); clickEvent.initEvent('click', false, true); } document.querySelector('BUTTON[type="button"]').dispatchEvent(clickEvent);
ایجاد و صدور رخدادهای سفارشی
فرض کنید در حال تهیهی کتابخانهای هستیم که افزودن و حذف آیتمها را به یک گالری عکس ارائه میدهد. میخواهیم روشی را در اختیار مصرف کننده قرار دهیم تا بتواند به این رخدادهای سفارشی (غیر استانداردی که جزو W3C نیستند) گوش فرا دهد.
در جیکوئری برای ایجاد رخدادهای سفارشی به صورت زیر عمل میشود:
// Triggers a custom "image-removed" element, // which bubbles up to ancestor elements. $libraryElement.trigger('image-removed', {id: 1});
در خارج از جیکوئری و توسط web API استاندارد مرورگرها ایجاد و صدور رخدادهای سفارشی به همراه bubbling آن به صورت زیر است:
var event = new CustomEvent('image-removed', { bubbles: true, detail: {id: 1} }); libraryElement.dispatchEvent(event);
var event = document.createEvent('CustomEvent'); event.initCustomEvent('image-removed', false, true, {id: 1}); libraryElement.dispatchEvent(event);
var event; // If the `CustomEvent` constructor function is not supported, // fall back to `createEvent` method. if (typeof CustomEvent === 'function') { event = new CustomEvent('image-removed', { bubbles: true, detail: {id: 1} }); } else { event = document.createEvent('CustomEvent'); event.initCustomEvent('image-removed', false, true, { id: 1 }); } libraryElement.dispatchEvent(event);
گوش فرادادن به رخدادهای صادر شده، توسط jQuery
در جیکوئری با استفاده از متد on آن میتوان به تمام رخدادهای استاندارد و همچنین سفارشی گوش فرا داد:
$(window).on('resize', function() { // react to new window size });
// remove all resize listeners - usually a bad idea $(window).off('resize');
روش بهتر انجام اینکار، ذخیرهی ارجاعی به متدی است که قرار است این رویداد گردانی را انجام دهد:
var resizeHandler = function() { // react to new window size }; $(window).on('resize', resizeHandler); // ...later // remove only our resize handler $(window).off('resize', resizeHandler);
همچنین اگر یک گوش فراهندهی به رخدادی تنها قرار است یکبار در طول عمر برنامه اجرا شود، میتوان از متد one استفاده کرد:
$(someElement).one('click', function() { // handle click event });
گوش فرادادن به رخدادهای صادر شده، توسط جاوا اسکریپت خالص (یا همان web API مرورگرها)
ابتداییترین روش گوش فرادادن به رخدادها که از زمان آغاز معرفی آنها در دسترس بودهاست، روش تعریف inline آنها است:
<button onclick="handleButtonClick()">click me</button>
روش دیگر ثبت رویدادگردان click، انتساب متد آن به خاصیت رخداد متناظری در آن المان ویژه است:
buttonEl.onclick = function() { // handle button click };
البته باید دقت داشت که یکی از دو روش یاد شده را میتوانید استفاده کنید. در اینجا آخرین رویدادگردان متصل شدهی به المان، همواره تمام نمونههای موجود دیگر را بازنویسی میکند.
اگر نیاز به معرفی رویدادگردانهای متعددی برای یک المان در ماژولهای مختلف برنامه وجود داشت، از زمان IE 9.0 به بعد، متد addEventListener برای این منظور تدارک دیده شدهاست و syntax آن بسیار شبیه به متد on جیکوئری است:
buttonEl.addEventListener('click', function() { // handle button click });
برای نمونه معادل قطعه کد جیکوئری که پیشتر با متد on نوشتیم، با جاوا اسکریپت خالص به صورت زیر است:
window.addEventListener('resize', function() { // react to new window size });
var resizeHandler = function() { // react to new window size }; window.addEventListener('resize', resizeHandler); // ...later // remove only our resize handler window.removeEventListener('resize', resizeHandler);
در اینجا حتی امکان تعریف متد one جیکوئری نیز پیش بینی شدهاست (البته جزو استانداردهای جدید وب از سال 2016 است):
someElement.addEventListener('click', function(event) { // handle click event }, { once: true });
var clickHandler = function() { // handle click event // ...then unregister handler someElement.removeEventListener('click', clickHandler); }; someElement.addEventListener('click', clickHandler);
کنترل انتشار رخدادها
فرض کنید میخواهیم جلوی انتخاب المانهای صفحه مانند تصاویر و متن را توسط ماوس بگیریم. روش انجام اینکار با jQuery به صورت زیر است:
$(window).on('mousedown', function(event) { event.preventDefault(); });
window.addEventListener('mousedown', function(event) { event.preventDefault(); });
برای جلوگیری کردن از انتشار رخدادی مانند click جهت رسیدن به سایر رویدادگردانهای ثبت شدهی در بین راه فاز bubbling، میتوان از متد stopPropagation استفاده کرد. روش انجام اینکار در جیکوئری:
$someElement.on('click', function(event) { event.stopPropagation(); });
و با web Api جهت جلوگیری از انتشار رخدادها در فاز capturing (این تنها راه مدیریت فاز capturing است):
// stop propagation during capturing phase someElement.addEventListener('click', function(event) { event.stopPropagation(); }, true);
// stop propagation during bubbling phase someElement.addEventListener('click', function(event) { event.stopPropagation(); });
$someElement.on('click', function(event) { event.stopImmediatePropagation(); });
someElement.addEventListener('click', function(event) { event.stopImmediatePropagation(); });
یک نکته: در این حالت اگر متد رویدادگردانی مقدار false را برگرداند، به معنای فراخوانی هر دوی متد preventDefault و stopPropagation است.
ارسال اطلاعات به رویدادگردانها
روش ارسال اطلاعات اضافی به رویداد گردانها در جیکوئری به صورت زیر است:
$uploaderElement.trigger('uploadError', { filename: 'picture.jpeg' });
$uploaderParent.on('uploadError', function(event, data) { showAlert('Failed to upload ' + data.filename); });
روش انجام اینکار با web API مرورگرها به صورت زیر است:
// send the failed filename w/ an error event var event = new CustomEvent('uploadError', { bubbles: true, detail: {filename: 'picture.jpeg'} }); uploaderElement.dispatchEvent(event); // ...and this is a listener for the event uploaderParent.addEventListener('uploadError', function(event) { showAlert('Failed to upload ' + event.detail.filename); });
و اگر میخواهید از IE هم پشتیبانی کنید، روش جایگزین کردن شیء CustomEvent با createEvent به صورت زیر است:
// send the failed filename w/ an error event var event = document.createEvent('CustomEvent'); event.initCustomEvent('uploadError', true, true, { filename: 'picture.jpeg' }); uploaderElement.dispatchEvent(event); // ...and this is a listener for the event uploaderParent.addEventListener('uploadError', function(event) { showAlert('Failed to upload ' + event.detail.filename); });
متوجه شدن زمان بارگذاری یک شیء در صفحه
در حین توسعهی برنامههای وب، با این نوع سؤالات زیاد مواجه خواهید شد: چه زمانی تمام و یا بعضی از المانهای صفحه کاملا بارگذاری و رندر شدهاند؟
پاسخ به این نوع سؤالات در W3C UI Events specification توسط رویداد استاندارد load داده شدهاست.
- چه زمانی تمام المانهای موجود در صفحه کاملا بارگذاری و رندر شده و همچنین شیوهنامههای تعریف شده نیز به آنها اعمال گردیدهاند؟
روش انجام اینکار با jQuery:
$(window).on('load', function() { // page is fully rendered });
window.addEventListener('load', function() { // page is fully rendered });
- چه زمانی markup استاتیک صفحهی جاری در جای خود قرار گرفتهاند؟
اهمیت این موضوع، به دسترسی به زمان مناسب و امن ایجاد تغییرات در DOM بر میگردد. برای این منظور رویداد استاندارد DOMContentLoaded پیشبینی شدهاست که زودتر از رویداد load، در دسترس برنامه نویس قرار میگیرد. در جیکوئری توسط یکی از دو روش معروف زیر به رویداد یاد شده دسترسی خواهید داشت:
$(document).ready(function() { // markup is on the page }); //or $(function() { // markup is on the page });
document.addEventListener('DOMContentLoaded', function() { // markup is on the page });
یک نکته: بهتر است این تعریف web API را پیش از تگهای <link> قرار دهید. زیرا بارگذاری آنها، اجرای هر نوع اسکریپتی را تا زمان پایان عملیات، سد میکند.
- چه زمانی المانی خاص در صفحه بارگذاری شدهاست و چه زمانی بارگذاری یک المان با شکست مواجه شدهاست؟
در جیکوئری توسط بررسی رویدادهای load و error میتوان به وضعیت نهایی بارگذاری المانهایی خاص دسترسی یافت:
$('IMG').on('load', function() { // image has successfully loaded }); $('IMG').on('error', function() { // image has failed to load });
document.querySelector('IMG').addEventListener('load', function() { // image has successfully loaded }); document.querySelector('IMG').addEventListener('error', function() { // image has failed to load });
- جلوگیری از ترک اتفاقی صفحهی جاری
گاهی از اوقات نیاز است برای از جلوگیری از تخریب صفحهی جاری و از دست رفتن اطلاعات ذخیره نشدهی کاربر، اگر بر روی دکمهی close بالای صفحه کلیک کرد و یا کاربر به اشتباه به صفحهی دیگری هدایت شد، جلوی اینکار را بگیریم. برای این منظور رخداد استاندارد beforeunload درنظر گرفته شدهاست. روش استفادهی از این رویداد در جیکوئری:
$(window).on('beforeunload', function() { return 'Are you sure you want to unload the page?'; });
window.addEventListener('beforeunload', function(event) { var message = 'Are you sure you want to unload the page?'; event.returnValue = message; return message; });
طراحی Url در Restful API
Url بخش اصلی و راه ارتباطی API شما با توسعه دهنده است .بنابراین طراحی یک ساختار مناسب و یکپارچه برای Url ها دارای اهمیت زیادی است .
Url پایه API خود را ساده و خوانا ، حفظ کنید . داشتن یک Url پایه ساده استفاده از API را آسان کرده و خوانایی آن را بالا میبرد و باعث میشود که توسعه دهنده برای استفاده از آن نیاز کمتری به مراجعه به مستندات داشته باشد. پیشنهاد میشود که برای هر منبع تنها دو Url پایه وجود داشته باشد . یکی برای مجموعه ای از منبع موردنظر و دیگری برای یک واحد مشخص از آن منبع . برای مثال اگر منبع موردنظر ما کتاب باشد ، خواهیم داشت :
.../books
.../books/1001
استفاده از این روش یک مزیت دیگر هم به همراه دارد و آن دور کردن افعال از Urlها است.
بسیاری در زمان طراحی Urlها و در نامگذاری از فعلها استفاده میکنند. برای هر منبعی که مدلسازی میکنید هیچ وقت نمیتوانید آن را به تنهایی و جداافتاده در نظر بگیرید. بلکه همیشه منابع مرتبطی وجود دارند که باید در نظر گرفته شوند. در مثال کتاب میتوان منابعی مثل نویسنده ، ناشر ، موضوع و ... را بیان کرد. حالا سعی کنید به تمام Url هایی که برای پوشش دادن تمام درخواستهای مربوط به منبع کتاب نیاز داریم فکر کنید . احتمالا به چیزی شبیه این میرسیم :
.../getAllBooks .../getBook .../newBook .../getNewBooksSince .../getComputerBooks .../BooksNotPublished .../UpdateBookPriceTo .../bookForPublisher .../GetLastBooks .../DeleteBook ….
پس حالا این درخواستهای متنوع را چطور با دو Url اصلی انجام دهیم ؟
1- از افعال Http برای کار کردن بر روی منابع استفاده کنید . با استفاده از افعال Http شامل POST ، GET ، PUT و DELETE و دو Url اصلی ، یک مجموعهی مناسب از عملیاتها در دسترس توسعه دهنده خواهد بود . به جدول زیر نگاه کنید .
توسعه دهندگان احتمالا نیازی به این جدول برای درک اینکه API چطور کار میکند نخواهند داشت.
2- با استفاده از نکته قبلی بخشی از Urlهای بالا حذف خواهند شد. اما هنوز با روابط بین منابع چکار کنیم؟ منابع تقریبا همیشه دارای روابطی با دیگر منابع هستند . یک روش ساده برای بیان این روابط در API چیست ؟ به مثال کتاب برمیگردیم. کتابها دارای نویسنده هستند. اگر بخواهیم کتابهای یک نویسنده را برگردانیم چه باید بکنیم؟ با استفاده از Urlهای پایه و افعال Http میتوان اینکار را انجام داد. یکی از ساختارهای ممکن این است :
GET .../authors/1001/books
POST .../authors/1001/books
3- بیشتر APIها دارای پیچیدگیهای بیشتری نسبت به Url اصلی یک منبع هستند . هر منبع مشخصات
و روابط متنوعی دارد که قابل جستجو کردن، مرتب سازی، بروزرسانی و تغییر هستند. Url
اصلی را ساده نگه دارید و این پیچیدگیها را به کوئری استرینگ منتقل کنید.
برای برگرداندن تمام کتابهای با قیمت پنچ هزار تومان با قطع جیبی که دارای امتیاز 8 به بالا هستند از کوئری زیر میشود استفاده کرد :
GET .../books?price=5000&size=pocket&score=8
و البته فراموش نکنید که لیستی از فیلدهای مجاز را در مستندات خود ارائه کنید.
4 - گفتیم که بهتر است افعال را از Url ها خارج کنیم . ولی در مواردی که درخواست ارسال شده در مورد یک منبع نیست چطور؟ مواردی مثل محاسبه مالیات پرداختی یا هزینه بیمه ، جستجو در کل منابع ، ترجمه یک عبارت یا تبدیل واحدها . هیچکدام از اینها ارتباطی با یک منبع خاص ندارند. در این موارد بهتر است از افعال استفاده شود. و حتما در مستندات خود ذکر کنید که در این موارد از افعال استفاده میشود..../convert?value=25&from=px&to=em .../translate?term=web&from=en&to=fa
5 - استفاده از اسامی جمع یا مفرد
با توجه به ساختاری که تا اینجا طراحی کرده ایم بکاربردن اسامی جمع بامعناتر و خواناتر است. اما مهمتر از روشی که بکار میبرید ، اجتناب از بکاربردن هر دو روش با هم است ، اینکه در مورد یک منبع از اسم منفرد و در مورد دیگری از اسم جمع استفاده کنید . یکدستی API را حفظ کنید و به توسعه دهنده کمک کنید راحتتر API شما را یاد بگیرد.
6- استفاده از نامهای عینی به جای نامهای کلی و انتزاعیAPI ی را در نظر بگیرید که محتواهایی را در فرمتهای مختلف ارائه میدهد. بلاگ ، ویدئو ، اخبار و .... حالا فرض کنیداین API منابع را در بالاتری سطح مدسازی کرده باشد مثل /items یا /assets . درک کردن محتوای این API و کاری که میتوان با این API انجام داد برای توسعه دهنده سخت است . خیلی راحتتر و مفیدتر است که منابع را در قالب بلاگ ، اخبار ، ویدئو مدلسازی کنیم .
PowerShell 7.x - قسمت سیزدهم - ساخت یک Static Site Generator ساده توسط PowerShell و GitHub Actions
├── _layout │ ├── _footer.html │ ├── _header.html │ ├── _nav.html │ └── main.html ├── build ├── img ├── posts └── set-posts.ps1
- دایرکتوری layout_: درون این دایرکتوری، ساختار اصلی بلاگ را قرار دادهایم. در ادامه محتویات هر فایل را مشاهده خواهید کرد:
<!--main.html--> <!DOCTYPE html> <html dir="rtl"> {{header}} <body> {{nav}} <main> {{content}} </main> {{footer}} </body> <!--_header.html--> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> <link href="https://cdn.jsdelivr.net/gh/rastikerdar/samim-font@v4.0.5/dist/font-face.css" rel="stylesheet" type="text/css" /> <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.16/dist/tailwind.min.css" rel="stylesheet"> <style> * { } </style> </head> <!--_nav.html--> <header> <nav> <div> <div> <a href="#">بلاگ من</a> </div> <div> <ul> {{nav}} </ul> </div> </div> </nav> </header> <!--_footer.html--> <footer> <div> <div> <p> تمامی حقوق محفوظ است </p> </div> </div> </footer>
- دایرکتوری build: درون این دایرکتوری، خروجیهای HTML که قرار است توسط اسکریپت PowerShell جنریت شوند، قرار خواهند گرفت. این پوشه در واقع قرار است توسط GitHub Pages میزبانی شود.
- دایرکتوری img: درون این دایرکتوری، تصاویر مربوط به هر بلاگپست را قرار خواهیم داد.
- دایرکتوری posts: درون این دایرکتوری، مطالبمان را با فرمت Markdown، قرار خواهیم داد. به عنوان مثال در ادامه یک نمونه از آن را مشاهده خواهید کرد (در کد زیر از Front Matter برای اضافه کردن یکسری متادیتای موردنیاز که حین بیلد شدن ضروری هستند استفاده شدهاست)
--- title: اولین پست من slug: hello date: 2023-04-26 author: سیروان عفیفی tags: [tag1, tag2, tag3] excerpt: این یک پست تستی است در مورد اینکه چطور میتوانیم از این قالب استفاده کنیم --- # اولین پست من ## اولین پست من ### اولین پست من #### اولین پست من ##### اولین پست من ###### اولین پست من لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی میباشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را میطلبد تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی و فرهنگ پیشرو در زبان فارسی ایجاد کرد. در این صورت میتوان امید داشت که تمام و دشواری موجود در ارائه راهکارها و شرایط سخت تایپ به پایان رسد وزمان مورد نیاز شامل حروفچینی دستاوردهای اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. <img src="/img/graphql.jpg"/>
- فایل set_post.ps1: موتور اصلی جنریت کردن صفحات HTML این فایل میباشد. در ادامه محتویات آن را مشاهده خواهید کرد. سپس هر کدام از توابع استفاده شده را یکییکی توضیح خواهیم داد:
Function Get-Layouts { $headerLayout = Get-Content -Path ./_layout/_header.html -Raw $homeLayout = Get-Content -Path ./_layout/main.html -Raw $footerLayout = Get-Content -Path ./_layout/_footer.html -Raw Return @{ Header = $headerLayout Home = $homeLayout Footer = $footerLayout } } Function Get-PostFrontMatter($postContent) { $frontMatter = [regex]::Match($postContent, "(---(?:\r?\n(?!--|\s*$).*)*)\s*((?:\r?\n(?!---).*)*\r?\n---)") Return $frontMatter } Function Set-Headings($postHtml) { Return $postHtml -Replace '<h(\d) id="(.*)">', { $level = $_.Groups[1].Value $id = $_.Groups[2].Value $class = Switch ($level) { '1' { 'text-4xl font-bold mb-2' } '2' { 'text-3xl font-bold mb-2' } '3' { 'text-2xl font-bold mb-2' } '4' { 'text-xl font-bold mb-2' } '5' { 'text-lg font-bold mb-2' } '6' { 'text-base font-bold mb-2' } } "<h$level class='$class' id='$id'>" } } Function ConvertTo-Slug { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string]$String ) process { $slug = $String -replace '[^\w\s-]', '' # remove non-word characters except hyphens $slug = $slug -replace '\s+', '-' # replace whitespace with a single hyphen $slug = $slug -replace '^-+|-+$', '' # remove leading/trailing hyphens $slug = $slug.ToLower() # convert to lowercase Write-Output $slug } } Function Get-Posts { $markdownPosts = Get-ChildItem -Path ./posts -Filter *.md $posts = @() Foreach ($post in $markdownPosts) { $postContent = Get-Content -Path $post.FullName -Raw $frontMatter = Get-PostFrontMatter $postContent $frontMatterObject = $frontMatter | ConvertFrom-Yaml $slug = $frontMatterObject.slug ?? (ConvertTo-Slug "$($frontMatterObject.date)-$($frontMatterObject.title)") $body = $postContent.Replace($frontMatter.Value, "") | ConvertFrom-Markdown $postHtml = $layouts.Home -replace '{{header}}', $layouts.Header ` -replace '{{title}}', $frontMatterObject.title ` -replace '{{nav}}', (Set-Navs) ` -replace '{{content}}', $body.Html ` -replace '{{footer}}', $layouts.Footer $postHtml = Set-Headings $postHtml $postHtml | Out-File -FilePath ./build/$slug.html $posts += @{ title = $frontMatterObject.title slug = $slug excerpt = $frontMatterObject.excerpt date = $frontMatterObject.date author = $frontMatterObject.author body = $body.Html } } Return $posts } Function Set-Archive { $posts = Get-Posts $archive = @() $archive = @" <ul> $($posts | ForEach-Object { "<li><a href='$($_.slug).html'>$($_.title)</a></li>" }) </ul> "@ Return $archive -join "`r`n" } Function Copy-ToBuild { $layouts = Get-Layouts $latestPosts = Get-Posts | ForEach-Object { @" <div> <img src="https://via.placeholder.com/300x200" alt="$($_.title)"> <h2>$($_.title)</h2> <p>$($_.excerpt)</p> <a href="$($_.slug).html">ادامه مطلب</a> </div> "@ } $homeLayout = $layouts.Home -replace '{{header}}', $layouts.Header ` -replace '{{nav}}', (Set-Navs) ` -replace '{{title}}', 'بلاگ من' ` -replace '{{content}}', ('<div>' + $latestPosts + '</div>') ` -replace '{{footer}}', $layouts.Footer $homeLayout | Out-File -FilePath ./build/index.html Copy-Item -Path ./img -Destination ./build -Recurse -Force } Function Set-Navs { $navs = @( @{ title = "صفحه اصلی" url = "/sample" }, @{ title = "درباره ما" url = "/sample/about.html" }, @{ title = "تماس با ما" url = "/sample/contact.html" } ) $navLayout = Get-Content -Path ./_layout/_nav.html -Raw $navLayout -replace '{{nav}}', ($navs | ForEach-Object { "<li><a href=""$($_.url)""text-gray-700 hover:text-gray-800 m-2"">$($_.title)</a></li>" }) } Copy-ToBuild
name: Deploy static content to Pages on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@v2 - name: Install powershell-yaml module shell: pwsh run: | Set-PSRepository PSGallery -InstallationPolicy Trusted Install-Module powershell-yaml -ErrorAction Stop - name: Setup Pages uses: actions/configure-pages@v3 - name: Build Static Site shell: pwsh run: | . ./set-posts.ps1 - name: Upload Static Site Artifact uses: actions/upload-pages-artifact@v1 with: path: build - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v2
- درون دایرکتوری posts، مطلب موردنظر را به همراه Front Matter زیر ایجاد کرده و سپس محتویات مطلب را بعد از آن وارد کنید:
--- title: اولین پست من slug: hello date: 2023-04-26 author: سیروان عفیفی tags: [tag1, tag2, tag3] excerpt: این یک پست تستی است در مورد اینکه چطور میتوانیم از این قالب استفاده کنیم --- content
- تغییرات ایجاد شده را کامیت و سپس پوش کنید. به محض پوش کردن تغییرات، GitHub Actions پروسه بیلد را انجام خواهد داد و بلافاصله میتوانید تغییرات را مشاهده نمائید.
کدهای این مطلب را میتوانید از اینجا دریافت کنید.