Cargo چیست و چه کاربردی دارد؟
Cargo همراه با زبان برنامه نویسی Rust گنجانده شده، همزمان نصب میشود و برای ایجاد، ساخت و مدیریت پروژههای Rust استفاده میگردد. این یک رابط سطح بالا برای کار با کدهای Rust را ارائه میدهد که شروع به کار با Rust و مدیریت پروژههای خود را برای توسعه دهندگان آسانتر میکند.
Cargo سیستم ساخت و package manager مخصوص زبان برنامه نویسی Rust است. ابزاری است که به توسعه دهندگان Rust کمک میکند تا پروژههای خود را با خودکارسازی کارهایی مانند کامپایل کد، مدیریت وابستگیها، اجرای آزمایشها و ایجاد بستههای قابل توزیع، مدیریت کنند.
Dependency management: برنامه Cargo میتواند بهطور خودکار وابستگیهای پروژههای Rust را دانلود کرده، بسازد و مدیریت کند. این باعث میشود توسعه دهندگان به راحتی کتابخانهها و ماژولهای جدیدی را به پروژههای خود اضافه کنند.
Building and testing: برنامه Cargo میتواند پروژههای Rust را بسازد و testها را به صورت خودکار اجرا کند. همچنین گزینههایی را برای ساختن ساختهای بهینه یا اشکال زدایی فراهم میکند.
Packaging: برنامه Cargo میتواند بستههای قابل توزیعی را مانند tarballs یا بستههای باینری را برای پروژههای Rust ایجاد کند.
Customization: برنامه Cargo به توسعه دهندگان اجازه میدهد تا فرآیند ساخت برنامهی خود را با تعیین گزینههای ساخت مختلف، در فایل پیکربندی Cargo.toml سفارشی کنند. بهطور کلی Cargo توسعه و مدیریت پروژههای Rust را با ارائه یک ابزار کارآمد برای خودکارسازی بسیاری از وظایف توسعه رایج، ساده میکند.
cargo new project_name
Cargo.toml : این فایل manifest پروژه است که در آن نام پروژه، نسخه، وابستگیها و سایر ابردادهها را مشخص میکنید. فایل Cargo.toml حاوی یک metadata درباره پروژه است؛ مانند نام، نسخه، نویسندگان و وابستگیهای آن. در اینجا مثالی از شکل ظاهری یک فایل Cargo.toml آورده شده است:
[package] name = "my-project" version = "0.1.0" authors = ["John Doe <johndoe@example.com>"] [dependencies] serde = "1.0" serde_json = "1.0"
بخش [dependencies] وابستگیهای پروژه را فهرست میکند. در این مثال، پروژه به پکیجهای serde و serde_json بستگی دارد که برای serialization و deserialization دادهها استفاده میشوند.
src directory: این دایرکتوری حاوی کد منبع پروژه شما است. به طور پیش فرض، شامل یک فایل main.rs است که نقطه ورود برنامه شما است.
target directory: این فهرست شامل فایلهای باینری کامپایل شده تولید شده توسط کامپایلر Rust میباشد.
هنگامی که cargo build یا cargo run را اجرا میکنید، Cargo به طور خودکار یک دایرکتوری target/debug را برای ذخیرهی فایلهای باینری کامپایل شده ایجاد میکند. اگر cargo build --release را اجرا کنید، Cargo بجای آن، یک دایرکتوری target/release را ایجاد میکند. بعلاوه، اگر هنگام ایجاد پروژهی خود از نسخهی خاصی از Rust (مانند نسخهی 2018) استفاده کنید، Cargo یک فیلد نسخه را در فایل Cargo.toml شما برای تعیین نسخه، اضافه میکند.
اسمبلیهای نام قوی در برابر دستکاری مقاوم هستند
از آنجائیکه محتویات اسمبلی، هش شده و مقدار هش آن امضا میشود، در نتیجه اگر شخصی به دستکاری اسمبلی اقدام کرده باشد یا اینکه فایل مد نظر آسیب دیده باشد، به راحتی قابل شناسایی است و آن اسمبلی به عنوان اسمبلی صحیح شناسایی نخواهد شد و نمیگذارد در GAC ثبت شود.
موقعیکه برنامه نیاز داشته باشد به اسمبلی نام قوی بایند یا متصل شود، از 4 ویژگی گفته شدهی در قسمت قبلی استفاده میکند تا آن را در GAC بیابد. اگر اسمبلی درخواستی موجود باشد، زیر دایرکتوری آن برگشت داده خواهد شد؛ ولی اگر آن را نیابد، ابتدا در داخل دایرکتوری برنامه و سپس در مسیرهایی که در فایل پیکربندی ذکر شدهاند، به دنبال آن خواهد گشت و در نهایت اگر برنامه توسط فایل MSI نصب شده باشد، محلهای توزیع را از طریق آن جویا خواهد شد و اگر باز به نتیجهای نرسد، اتصال ناموفق گزارش شده و خطای زیر را ایجاد خواهد کرد:
System.IO.FileNotFoundException
System.IO.FileLoadExceptio
- جلوگیری از دستکاری و حفظ امنیت آن
- این اسمبلی تنها یکبار از حافظهی فیزیکی استفاده میکند؛ برعکس توزیع خصوصی که برای هر برنامه باید یک فضای دیسکی داشته باشد.
- توزیع سادهتر برای نسخههای آینده فراهم میشود که بعدا در مورد آن توضیح میدهیم.
سیاستهای انتخاب اسمبلی توسط GAC
موقعی که GAC میخواهد یک اسمبلی را برای برنامه شما بازگرداند، از روی خصوصیاتی چون نام اسمبلی، ورژن، فرهنگ، توکن کلید عمومی و در نهایت بسته به معماری ماشین، یک اسمبلی را برمیگرداند. در صورتیکه اسمبلی خاصی را برای ماشین مورد نظر پیدا نکند، از اسمبلی یک ماشین دیگر استفاده خواهد کرد. در واقع این انتخاب، یک سیاست پیش فرض است که میتواند از طریق ناشر یا مدیر سیستم تغییر پیدا کند و رونویسی شود.
کنترل مدیریت پیشرفته (پیکربندی)
در قسمت بیستم با ساخت فایل پیکریندی و نحوه تنظیم کردن اسکن اسمبلیها در CLR آشنا شدیم. این بار قصد داریم در مورد المانهای دیگر این فایل پیکریندی صحبت کنیم. فایل پیکربندی زیر را بررسی میکنیم:
<?xml version="1.0"?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemasmicrosoftcom:asm.v1"> <probing privatePath="AuxFiles;bin\subdir" /> <dependentAssembly> <assemblyIdentity name="SomeClassLibrary" publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/> <bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" /> <codeBase version="2.0.0.0" href="http://www.Wintellect.com/SomeClassLibrary.dll" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="TypeLib" publicKeyToken="1f2e74e897abbcfe" culture="neutral"/> <bindingRedirect oldVersion="3.0.0.03.5.0.0" newVersion="4.0.0.0" /> <publisherPolicy apply="no" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
Probing | در
قسمت بیستم گفتیم که در این المان، مکانهایی را که CLR برای پیدا کردن
اسمبلیهای با نام ضعیف، باید اسکن کند، وارد میکنیم که هر مسیر با , از هم جدا شدهاست. برای اسمبلیهای با نام قوی CLR باید داخل GAC را نگاه
کند و در URL هایی که از طریق المان CodeBase مشخص کردهایم. اگر المان
CodeBase مشخص نشود، CLR برای پیدا کردن اسمبلیهای با نام قوی، داخل
دایرکتوریهای این المان را اسکن خواهد کرد. |
Dependent Assembly اول | در
اولین فرزند این المان، Assembly Identity یک اسمبلی با مشخصاتی مثل
Culture و توکن عمومی معرفی میشود و در BindingRedirect به CLR اطلاع
میدهد موقعی که به دنبال نسخه یک این اسمبلی است، نسخهی دو آن را برای
استفاده جایگزین کند. |
Code Base | این
المان میگوید که وقتی CLR سعی در پیدا کردن نسخهی 2 اسمبلی را دارد، آن را
از طریق آدرس مورد نظر پیدا کند. این المان میتواند برای اسمبلیهای با نام
ضعیف هم کار کند. |
Dependent Assembly دوم | این مورد هم همانند سابق است با این تفاوت که گسترهی نسخه 3 تا 3.5 را به نسخهی 4 تغییر میدهد. |
Publisher Policy | اگر
سازمان تولید کننده این اسمبلی فایلی برای تعیین Policy به همراه اسمبلی
ارائه کرده باشد، این المان باعث میشود این فایل ندیده گرفته شود (در مورد
فایل Policy در آینده صحبت میکنیم). |
با وجود این حالت اگر فرض کنیم مدیر یک سیستم متوجه شود که اسمبلی برنامه دچار مشکل شده است و با ناشر تماس بگیرد و ناشر نسخهی جدیدی از آن اسمبلی را در اختیار او بگذارد، مدیر سیستم میتواند به راحتی از طریق فایل پیکربندی، CLR را به استفادهی از اسمبلی جدید به جای اسمبلی قدیمی هدایت کند.
نکته : اگر مدیر بخواهد تمام برنامههای موجود از این اسمبلی جدید استفاده کنند باید فایل machine.config را ویرایش کند.
Complete Guide to Open Source - How to Contribute
⭐️ Course Contents ⭐️
⌨️ (00:00) Introduction
⌨️ (01:11) What is Open Source
⌨️ (01:46) Why you should care about Open Source
⌨️ (04:06) What is Git
⌨️ (04:56) What is GitHub
⌨️ (05:24) Example custom GitHub profile
⌨️ (06:01) GitHub features
⌨️ (13:37) GitHub Actions for Continuous Integration (CI)
⌨️ (14:49) Insights tab for more project information
⌨️ (15:04) GitHub Discussions for threaded conversations
⌨️ (15:41) GitHub Projects board like Trello
⌨️ (16:10) GitHub Wiki
⌨️ (17:15) How to find Open Source projects
⌨️ (19:40) How to write Markdown
⌨️ (27:58) Draft a Pull Request (PR)
⌨️ (29:06) Make money directly with GitHub Sponsors
⌨️ (30:15) Make money indirectly from Open Source
⌨️ (32:19) freeCodeCamp.org Open Source resources
⌨️ (34:04) Everyone is a Project Maintainer
⌨️ (39:49) How to customize your GitHub profile
⌨️ (40:46) Conclusion
Cake v1.0.0 منتشر شد
Expression<Func<string, bool>> f = s => s.Length < 5;
منبع : کتاب C# 8 in a Nutshell
ParameterCollection به پارامترهای استفاده شده در فیلتر اشاره دارد که در فیلتر بالا فقط s استفاده شدهاست و از نوع string است.
BinaryExpression شامل سه قسمت مهم Left , Right و NodeType میباشد. برای فیلتر بالا، مقدار پراپرتی Left برابر s.Length میباشد و پراپرتی Right شامل مقدار 5 و مقدار NodeType هم برابر LessThan میباشد. یعنی فیلتر بالا به یک درخت تبدیل شده که نود اصلی آن LessThan است و دو مقدار Left و Right را باهم مقایسه میکند. اما اگر یک شرط دیگر را به فیلتر بالا اعمال کنیم، ساختار Expression کمی تغییر میکند. برای مثال:
Expression<Func<string, bool>> filter = s => s.Length > 5 && s.Length < 45;
Expression ایجاد شده برای این فیلتر شامل همان ساختار قبلی است؛ اما با این تغییر که هر کدام از پراپرتیهای Right و Left، خود یک BinaryExpression شدهاند و مقدار NodeType اصلی از LessThan به AndAlso تغییر پیدا کردهاست. Expression ایجاد شده از فیلتر بالا ( filter.Body ) به این صورت است که پراپرتی Left آن برابر است با یک BinaryExpression که مقدار NodeType آن برابر است با GreaterThan و پراپرتی Left آن شامل s.Length میباشد و پراپرتی Right آن برابر 5 میباشد. همچنین پراپرتی Right مربوط به filter.Body برابر یک ExpressionBinary است که مقدار NodeType آن برابر است با LessThan و پراپرتی Left آن برابر s.Length است و پراپرتی Right آن برابر 45 میباشد.
filter.Body شبیه به تصویر زیر میباشد :
اگر بخواهیم خودمان یک Expression tree را ایجاد کنیم، باید از پایینترین نود آن شروع کنیم. یعنی ابتدا باید پراپرتی Left و Right را ایجاد کنیم و سپس این دو پراپرتی را با هم مقایسه کنیم (NodeType). در کد زیر Expression مربوط به فیلتر بالا را نوشتهایم:
ParameterExpression parameterExpression = Expression.Parameter(typeof(string)); MemberExpression memberExpression = Expression.Property(parameterExpression, "Length"); ConstantExpression greaterThanConstantExpression = Expression.Constant(5); BinaryExpression greaterThanComparison = Expression.GreaterThan(memberExpression, greaterThanConstantExpression); var greaterThan = Expression.Lambda<Func<string, bool>>(greaterThanComparison, parameterExpression); ConstantExpression lessThanConstantExpression = Expression.Constant(45); BinaryExpression lessThanComparsion = Expression.LessThan(memberExpression, lessThanConstantExpression); var lessThan = Expression.Lambda<Func<string, bool>>(lessThanComparsion, parameterExpression); var param = Expression.Parameter(typeof(string), "x"); var body = Expression.AndAlso( Expression.Invoke(greaterThan, param), Expression.Invoke(lessThan, param) ); Expression<Func<string, bool>> filter = Expression.Lambda<Func<string, bool>>(body, param);
ParameterExpression : نوع پارامتری را که میخواهیم روی آن شرط را روی آن اعمال کنیم، مشخص کردهایم.
MemberExpression : پراپرتی Length را معرفی کردهایم که قرار است شرطی بر روی این پراپرتی اعمال شود.
ConstantExpression : مقدار ثابتی که پراپرتی MemeberExpression قرار است با آن مقایسه شود.
BinaryExpression : نود تایپ را مشخص کردهایم که برابر است با GreaterThan.
سپس Expression مربوط به هرکدام را در greaterThan و lessThan ایجاد کردهایم و این دو را باهم And کرده و در متغییر body قرار دادهایم و در نهایت filter را با دستور Expression.Lambda ایجاد کردهایم که برابر است با :
Expression<Func<string, bool>> filter = s => s.Length > 5 && s.Length < 45;
ساخت یک داینامیک فیلتر
در ادامه میخواهیم یک داینامیک فیلتر را ایجاد کنیم که به طور مثال برنامه نویس از سمت فرانتاند بتواند فیلترهای سادهای را اعمال کند. برای این کار یک کلاس برای فیلتر ایجاد میکنیم :
public class DynamicModel { public string Name { get; set; } public string Comparison { get; set; } public object Data { get; set; } }
پراپرتی Data مقداری است که باید با آن مقایسه انجام شود.
Comparison نوع عملیات را مشخص میکند مانند : Equal, LessThan, GreaterThan و... .
پراپرتی Name نام پراپرتی است که باید شرط روی آن اعمال شود.
کلاس ثابت ها:
public static class ComparisonConstant { public const string LessThan = "LesThan"; public const string LessThanEqual = "LesThanEqual"; public const string GreaterThan = "GreaterThan"; public const string GreaterThanEqual = "GreaterThanEqual"; public const string Equal = "Equal"; public const string NotEqual = "NotEqual"; }
ساخت اکستنشن متد:
public static IQueryable<TModel> DynamicFilter<TModel>(this IQueryable<TModel> iqueryable, IEnumerable<DynamicModel> dynamicModel) { return iqueryable.Where(Filter<TModel>(dynamicModel)); }
public static Expression<Func<TModel, bool>> Filter<TModel>(IEnumerable<DynamicModel> dynamicModel) { Expression<Func<TModel, bool>> result = a => true; foreach (var item in dynamicModel) { ParameterExpression parameterExpression = Expression.Parameter(typeof(TModel)); MemberExpression memberExpression = Expression.Property(parameterExpression, item.Name); ConstantExpression constantExpression = Expression.Constant(item.Data); BinaryExpression comparison = GetBinaryExpression(item.Comparison, memberExpression, constantExpression); var expression = Expression.Lambda<Func<TModel, bool>>(comparison, parameterExpression); var param = Expression.Parameter(typeof(TModel), "x"); var body = Expression.AndAlso( Expression.Invoke(result, param), Expression.Invoke(expression, param) ); result = Expression.Lambda<Func<TModel, bool>>(body, param); } return result; }
ورودی این مدل، لیستی از DynamicModel میباشد که به ازای هر کدام از آیتمها، یک BinaryExpression ایجاد میکند و آن را با result تعریف شده And میکند. یعنی تمامی آیتمهای ارسال شده باهم And میشوند.
متد GetBinaryExpression بر اساس مقدار فیلد Comparison که از سمت فرانت ارسال میشود، کار میکند:
private static BinaryExpression GetBinaryExpression(string comparison, MemberExpression memberExpression, ConstantExpression constantExpression) { switch (comparison) { case ComparisonConstant.Equal: return Expression.Equal(memberExpression, constantExpression); case ComparisonConstant.LessThan: return Expression.LessThan(memberExpression, constantExpression); case ComparisonConstant.GreaterThan: return Expression.GreaterThan(memberExpression, constantExpression); case ComparisonConstant.NotEqual: return Expression.NotEqual(memberExpression, constantExpression); case ComparisonConstant.GreaterThanEqual: return Expression.GreaterThanOrEqual(memberExpression, constantExpression); case ComparisonConstant.LessThanEqual: return Expression.LessThanOrEqual(memberExpression, constantExpression); default: return null; } }
کلاس Category را در نظر بگیرید که شامل دو پراپرتی Title و Id میباشد و میخواهیم از این داینامیک فیلتر، برای فیلتر کردن دیتاها استفاده کنیم از سمت فرانتاند. اگر از سمت فرانتاند چنین دیتایی ارسال شود:
[ { "Name":"Title", "Comparison":"Equal", "Data":"Hi" }, { "Name":"Id", "Comparison":"LesThanEqual", "Data": 100 } ]
تمامی رکوردهایی که مقدار پراپرتی Title آنها برابر Hi باشد و Id آن کوچکتر مساوی 100 باشد، از دیتابیس خوانده میشود.
var categories = _dbContext.Categories .DynamicFilter(filter)//filter => IEnumerable<DynamicModel> .ToList();
گیت هاب داینامیک فیلتر
Gulp #3
نصب bower
- ساده کردن تعریف وابستگیهای منابع پروژه با تعریف یک فایل bower.json
- نیازی به commit کردن واستگیهای پروژه نیست.
- با ذکر ورژن مربوط به وابستگی یا محدودهی قابل قبول برای آن، به روز رسانی منابع به سادگی با یک دستور انجام میشود.
- وابستگی های وابسته به یک منبع را نیز نصب میکند. برای مثال زمانیکه بوت استرپ را به عنوان وابستگی پروژه تعریف میکنیم، وابستگی آن یعنی jquery را چون در فایل bower.json بوت استرپ تعریف شدهاست، به صورت خودکار دانلود میکند.
- در نهایت افراد هم تیمی یا توسعه دهندگان دیگر به راحتی با زدن دستور bower install تمام وابستگیهای پروژه را میتوانند نصب کنند.
sudo npm install -g bower bower init
bower install bootstrap-sass-official --save bower install fontawesome --save bower install bootstrap-rtl --save
نصب پلاگینهای مورد نیاز gulp
sudo npm install gulp gulp-ruby-sass gulp-notify gulp-bower --save-dev
نوشتن تسکها برای گالپ
var gulp = require('gulp'), sass = require('gulp-ruby-sass'), notify = require('gulp-notify'), bower = require('gulp-bower');
var config = { sassPath = './resources/sass', bowerDir = './bower_components' }
// create a task to do bower install gulp.task('bower', function() { return bower() .pipe(gulp.dest(config.bowerDir)) });
// Copy js files to public folder gulp.task('js', function() { return gulp.src([config.bowerDir + '/bootstrap-sass-official/assets/javascripts/bootstrap.min.js', config.bowerDir + '/jquery/dist/jquery.min.js' ]) .pipe(gulp.dest('./public/js')); }); // Copy fontawesome icons to public/fonts folder gulp.task('icons', function() { return gulp.src(config.bowerDir + '/fontawesome/fonts/**.*') .pipe(gulp.dest('./public/fonts')); });
gulp.task('css', function() { return sass(config.sassPath + '/style.scss', { // Our coustom sass style: 'compressed', // minify css loadPath: [ // load paths to easy use import in resources/sass './resources/sass', config.bowerDir + '/bootstrap-sass-official/assets/stylesheets', // bootstrap sass files config.bowerDir + '/fontawesome/scss' // awesome icons sass files ] }) });
gulp.task('css', function() { return sass(config.sassPath + '/style.scss', { // Our coustom sass style: 'compressed', // minify css loadPath: [ // load paths to easy use import in resources/sass './resources/sass', config.bowerDir + '/bootstrap-sass-official/assets/stylesheets', // bootstrap sass files config.bowerDir + '/fontawesome/scss' // awesome icons sass files ] }) .on('error', notify.onError(function(error) { return 'Error: ' + error.message; })) .pipe(gulp.dest('./public/css')); });
// Rerun the task when a file changes gulp.task('watch', function() { gulp.watch(config.sassPath + '/**/*.scss', ['css']); });
// Run this task with : gulp // OR gulp default gulp.task('default', ['bower', 'icons', 'css','js']);
مخزن گیت هاب