theKindOfMe

January 26, 2012

Rails Assets Pipeline Survival Guide

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 1:50 am

Helpful settings

If you have assets pipeline enabled. By default you need to precompile your assets every time you change something (in production env). Having everything precompiled is cool but this will ask rails compile any assets that are not already compiled.

# Don't fallback to assets pipeline if a precompiled asset is missed
 config.assets.compile = true

Page Specific JS/CSS

If you want some JS/CSS to only be included in a specific set of pages.

  • break the page specific JS/CSS code to different files (ex:- profiles.js)
  • make sure they are not referenced in your manifest file (application.js)
  • add the following code snippets to the necessary files
application.html.erb (your main template)
<head>
 <title>FamousApp</title>
 <%= stylesheet_link_tag "application" %>
 <%= javascript_include_tag "application" %>
 <%= yield :head %>
 <%= csrf_meta_tags %>
</head>
specific_page.html.erb (the template where you want your JS to appear)
<% content_for :head do %>
 <%= javascript_include_tag "profiles" %>
<% end %>

production.rb

# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
 config.assets.precompile += %w( profiles.js )

Deploying with Capistrano

This is easy enough

load 'deploy/assets'

If you ever run across this problem:

# Disable warning message about missing dirs 'public/[javascripts|images|stylesheets]'
# src: https://github.com/capistrano/capistrano/issues/79
set :normalize_asset_timestamps, false

January 27, 2011

Switching Devise Mail Sender Address with the Environment

Filed under: Uncategorized — Tags: , , — yasi8h @ 7:04 am

This is something very simple that i took more than 5 minutes to find out 🙂

in file: config/initializers/devise.rb

if Rails.env.development?
config.mailer_sender = “no-reply@development.com”
elsif Rails.env.production?
config.mailer_sender = “no-reply@production.com”
end

Rails.env does the trick.

July 7, 2010

Ad-Hoc Build – Zip – Rename – Deploy ‘Automation’

Filed under: Uncategorized — Tags: , , — yasi8h @ 8:15 am

At work i wanted to automate the build releasing process (of mostly c# desktop/mobile applications which are developed using VS and distributed as installers). This is basically giving the QA a fresh versioned build of whatever the software that is being developed. Although it sound pretty easy and straightforward. It is not. And i have come to hate doing this manually, because it takes a lot of time and its repetitive work, so its very… boring!. The following CLI program that i wrote just automates:

  • Update the working folder from the SVN repo
  • Clean
  • Build (using Release config).
  • Zip (only the Installer(s) resulting from the final build).
  • Extract the version of the resulting executables by reading the AssemblyInfo.cs of the main project.
  • Name the zip file according to all the conventions (boring…).
  • Copy it over to the QA’s SMB share, and put it in a folder structure (again some more naming conventions come in to play).

I know this is not the nicest way of doing this. I should probably be using MSBuild/nAnt/a continues integration server…etc to do it. But, it felt a bit more complicated, and it required me to spend a little bit of time learning stuff. So… i ended up writing this.

#region

using System;
using System.Diagnostics;
using System.IO;

#endregion

namespace auto_build
{
    internal class Program
    {
        private const string Title = "auto_build v0.01";

        private static void Main(string[] args)
        {
            Console.Title = Title;
            var settings = Settings.Default;

            PSeperator();
            P(Title);

            PSeperator();
            P("Settings");

            PSeperator();
            P("Updating Source from SVN...");
            Run(settings.SvnPath, string.Format(settings.SvnUpdateCommand, settings.SolutionRoot));

            PBlankLine();
            P("Cleaning Solution...");
            Run(settings.DevenvPath, string.Format(settings.DevenvClean, settings.SolutionPath, settings.SolutionName));

            PBlankLine();
            P("Building (Release Config) Solution...");
            Run(settings.DevenvPath,
                string.Format(settings.DevenvBuildRelease, settings.SolutionPath, settings.SolutionName));

            PBlankLine();
            P("Zipping the Setup...");
            //ex: "NAME-SG v1.0.0.3106 Setup 06-Jul-2010 10-40"
            var zipFilePath = string.Format("{0}\\Release", settings.SetupProjectPath);
            var zipFileName = string.Format(settings.ZipFileNameFormat, settings.ZipFileApplicationName,
                                            GetProjectVersion(),
                                            DateTime.Now.ToString(settings.ZipFileNameDateTimeFormat));
            Run(settings.WinrarPath,
                string.Format(settings.WinrarPathArgsForCreatingAnArchive, zipFileName, zipFilePath));

            PBlankLine();
            P("Coping the zipped archive to the destanation...");
            var directory =
                Directory.CreateDirectory(string.Format("{0}//v{1}", settings.DestPath, GetProjectVersion()));
            File.Move(string.Format("{0}//{1}", zipFilePath, zipFileName),
                      string.Format("{0}//{1}", directory.FullName, zipFileName));

            PBlankLine();
            P("Done!");
            P("Press any key to exit...");
            Console.ReadLine();
        }

        private static string GetProjectVersion()
        {
            using (
                var reader =
                    new StreamReader(
                        new FileStream(string.Format(Settings.Default.AssemblyInfoPath, Settings.Default.SolutionPath),
                                       FileMode.Open, FileAccess.Read)))
            {
                while (!reader.EndOfStream)
                {
                    var line = reader.ReadLine();
                    if (!line.Contains("// [assembly: AssemblyVersion(\"") &&
                        line.Contains("[assembly: AssemblyVersion(\""))
                    {
                        var start = line.IndexOf("[assembly: AssemblyVersion(\"") +
                                    "[assembly: AssemblyVersion(\"".Length;
                        var end = line.IndexOf("\")]");
                        var length = end - start;

                        return line.Substring(start, length);
                    }
                }
            }

            return "x.x.x.x"; //couldn't find the assebmly version
        }

        private static string Run(string command, string paras)
        {
            var pProcess = new Process
                               {
                                   StartInfo =
                                       {
                                           FileName = command,
                                           Arguments = paras,
                                           UseShellExecute = false,
                                           RedirectStandardOutput = true
                                       }
                               };
            pProcess.Start();

            //Get program output
            var strOutput = pProcess.StandardOutput.ReadToEnd();
            pProcess.WaitForExit();
            return strOutput;
        }

        #region Console Text Output Helpers

        private static void P(string text)
        {
            Console.WriteLine(text);
        }

        private static void PBlankLine()
        {
            P(string.Empty);
        }

        private static void PSeperator()
        {
            PBlankLine();
            P("------------------------------------------------------");
            PBlankLine();
        }

        #endregion
    }
}

The program above uses a settings file to get most of the information it requires. you can get the settings file and the above posted file here.

May 15, 2010

A Ruby Script to Organize Digital Photos in to a Folder Hierarchy by Date/Time Taken

Filed under: Uncategorized — Tags: , — yasi8h @ 5:26 pm

So i was at home with nothing else to do. I had my new “NAT” with plenty of empty space and my collection of photos. Although when viewing with Picasa there photos were browse-able, when viewing it through a file explorer like nautilus, It was very hard browse them. So i wanted to organize them in to a folder structure that makes some sense. And this is what i came up with. Hope it will be of some use to someone.

Copies digital photos to a directory structure orgnazied by year/month/day/time.ext...etc

Folder Structure

Get it from github: http://github.com/thekindofme/Photo-Organizer

Photo Organiser

A Ruby script to copy and arrange your digital photos to a folder structure like /Root/Year/Month/Day/Time.jpg … It will use EXIF timestamp if its there or else take file creation timestamp.

Example

    bash> ruby photo_org.rb /path/to/source /path/to/destination dirs

Installation

  • Get the source.
  • Install exifr (gem install exifr) if you don’t have them already.

Notes

  • If there are two or more files with the same timestamp they will be copied as: file_name.jpg, file_name-1.jpg, file-name-2.jpg…etc
  • Duplicates will be copied to /path/to/dest/Duplicates
  • Non jpeg files will be copied to /path/to/dest/Other
  • The script creates a log file in the destination directory at the end of execution.
  • You can edit more settings like folder names, folder structure, file name…etc by editing the constants inside the script
  • Any feedback will be highly appreciated.



April 1, 2010

Getting a PropertyInfo from a PropertyDescriptor in .NET

Filed under: Uncategorized — Tags: , , — yasi8h @ 5:57 am
PropertyDescriptor pd;
PropertyInfo pi;

//when pd is actually pointing to some real Property Descriptor object
pi = pd.ComponentType.GetProperty(pd.Name);

January 29, 2010

Gmail Attachment Downloader Script

Filed under: Uncategorized — Tags: , — yasi8h @ 1:54 am

<rant>I wanted to download all the attachments from all the emails in my account, that have a label x on them. With all the cool libs around this should be a piece of cake. But… After a lot of googling, and spending hours installing different versions of the ruby runtimes (found out about RVM in the process, this is very useful), and going through old gems that spits out so many errors when they are run on 1.9 runtimes… etc, came up with this one, that among other things, just work!. One thing that i am again reminded is that its not easy coming back to ruby development when you don’t have a good working environment set up. Installing (it’s more about knowing what dependencies you need for different gems i guess) every thing that is required on Ubuntu is not that simple either.</rant>

How to Run

  • Edit the script to reflect your account and other settings.
  • Install the nesseary gems (gem install tmail)

Things you should know about

  • This was tested on ruby 1.8.7 (2009-06-12 patchlevel 174)
  • You need tmail -> gem install tmail
  • Your Gmail account should be IMAP enabled (more info).
  • The script will first go through all the mails with some label applied on them. And it will download the attachments on these mails one by one. After it have processed one email, it will put on another label on it, to signify that this message’s attachments have been downloaded. So you could continue this script from where it stopped, if the script crashes out in the middle of the process.
  • I first tried doing this using ruby-gmail gem, but for some reason it fails to detect the attachments. But this may be fixed when you are reading this post. So if you want a more simpler script with more gmail oriented functionalities have a look at that project.
  • Whatever you do, do not violate Gmail’s ToS.

Issues

  • This process emails one by one, it is not multi threaded. And it will take sometime. So be patient and let it do its job.
  • When a certain email is processed and its attachments are downloaded the script will attach a label to it. This might/might not remove the existing labels from that email. If it does not remove them, you will need to do so manually, if and when you are starting the script to resume its work from where it stopped last time.
  • The script doesn’t know about (gmail) threads, it processes individual emails.

I am giving up, its too hard to post source code on wordpress, get it from git hub

#! /usr/bin/ruby
#This script is baised on http://snippets.dzone.com/posts/show/7530

username = "username@gmail.com"
password = "pa$sword"
look_in_folder = "gmail_label_of_the_messages_whose_attachments_you_want_downloaded"
move_downloaded_mails_to_folder = "gmail_label_to_be_applied_for_processed_emails"
save_to_folder = "/path/to/folder/where/attachments/will/be/saved"

require 'net/imap'
require 'rubygems'
require 'tmail'

# This is a convenience monkey patch
class Net::IMAP
 def uid_move(uid, mailbox)
 uid_copy(uid, mailbox)
 uid_store(uid, "+FLAGS", [:Deleted])
 end
end

puts 'Starting...'
imap = Net::IMAP.new('imap.gmail.com', '993', true)

puts "Logging in as #{username} ..."

imap.login(username, password)
imap.select(look_in_folder)
mails = imap.uid_search(["NOT", "DELETED"])

puts "Found #{mails.count} mail(s) in folder '#{look_in_folder}'"

puts "\nFetching the next email (this might take some time depending on the size of the message/attachment..."
mails.each do |uid|
 # save_attachment
 mail = TMail::Mail.parse( imap.uid_fetch(uid, 'RFC822').first.attr['RFC822'] )
 puts "Processing '#{mail.subject}'"
 if ! mail.attachments.blank?
 puts "Detected #{mail.attachments.count} attachment(s)"
 mail.attachments.each {|attachment|
 puts "Saving attachment to '#{attachment.original_filename}'..."
 File.open(save_to_folder + attachment.original_filename,"w+") { |local_file|
 local_file << attachment.gets(nil)
 }
 }

 end

 # archive mail to mailbox
 puts "Moving '#{mail.subject}' to folder '#{move_downloaded_mails_to_folder}'"
 imap.uid_move(uid, move_downloaded_mails_to_folder)
end

imap.expunge
puts "Logging out..."
imap.logout

References

July 1, 2009

Using Custom T4 templates To Generate Better Controllers in ASP MVC

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 7:52 am

While working on this asp.net mvc project at work i couldn’t help notice that there are lots of code that is getting copy-pasted from one controller to the other, when i am adding a new controller. now as you know mvc fm comes with a set of T4 templates that lets you add views and controllers in such a way that some code in these views and controllers are generated for you. this is grate. but you can customize the original templates to your needs and make them generate more of the code for you. (please note that i am talking about controllers that are responsible for mostly CURD on a given single model.)

First of all you will need to identify what code gets repeated from controller to controller in your asp.net mvc project. and then you can get these stuff out in to a template and start working on it.

Locating the T4 Templates

you can find your original templates in [Visual Studio Install Directory]\Common7\IDE\ItemTemplates\[CSharp | VisualBasic]\Web\MVC\CodeTemplates\ and the you can copy them to your project folder. you can start editing these copies and they will be used when you invoke your templates(ex:- by clicking on the Add Controller sub menu within the IDE). for more info please refer to T4 Templates: A Quick-Start Guide for ASP.NET MVC Developers )

Customizing

In my case i wanted my controllers to have all the basic actions like List, Details, Create, Edit and my custom actions like Grid, GridData(take a look at A Grid with Ajax/Pagination/Sorting/Filtering on ASP.net MVC with ExtJS and Enitiy Framework).

i needed to get the type of the model that a given controller is associated with. and then i have to traverse it’s properties(through reflection…etc) and do some string manipulation to generate custom method signatures and custom anonymous types. i also wanted to filter the list of properties that will be used in these parameters and anon types.

Action method signatures

action method signatures can be different from controller to controller. as in…

//Different Properties, in different models
//
//Create action of a Product Controller
public ActionResult Create(string ID, string Name, string Description, string Price)
//
//Create action of a User Controller
public ActionResult Create(string ID, string Name, string Age)

//Different Primary Keys in different models
//
public ActionResult Details(int UID)
//
public ActionResult Details(int UID, int SiteUID)

Method calls to models

//Different Primary Keys
//
public ActionResult Details(int UID)
        {
            return View(Category.GetModelByPrimaryKey(new {UID}));
        }
//
public ActionResult Details(int UID, int SiteUID)
        {
            return View(Category.GetModelByPrimaryKey(new {UID, SiteUID}));
        }

//Different Properties for different models
//
public ActionResult Create(string ID, string Name)
        {
            Category.Create(new {ID,Name}, User.Identity.Name)
            return View();
        }
//
public ActionResult Create(string ID, string Name, int PropOne, string PropTwo)
        {
            Category.Create(new {ID,Name, PropOne, PropTwo}, User.Identity.Name)
            return View();
        }

But unlike in the view here in the controller you can’t just access the model’s type through MvcTextTemplateHost.ViewDataType. when you are generating a controller this property is null. so i thought of using reflection get the model for the current controller. you can use the ControllerRootName property of MvcTextTemplateHost(available through mvcHost variable in the default template) to get the model name(ex: ProductController would give Product).

<#
Type controllerType = Assembly.LoadFile("D:\\path\\to\\your\\model\\assemblys\\dll\\file.dll").GetType("Example.Models." + mvcHost.ControllerRootName);
#>

Following is the full source code of a generated controller and the modified template that was used to generate it. I have reused some methods like IsBindableType and GetEntityKeyProperties(i have modified this a bit) that can be found in some View templates in mvc framework. The generated controller can be used to do CURD operations on the model and provide a backend(providing data to the grid through json) for a ExtJS grid! 🙂 (A Grid with Ajax/Pagination/Sorting/Filtering on ASP.net MVC with ExtJS and Enitiy Framework)

Generated Controller

//
//A Generated Controller
//
using System;
using System.Collections;
using System.Web.Mvc;
using ExampleInc.Filters;
using ExampleInc.Lib.Helpers;
using ExampleInc.Models;
using ExampleInc.Models.UoW;

namespace ExampleInc.Controllers
{
	[Authorize]
    [NavigationFilter]
    public class ExampleModelController : Controller
    {
        //
        // GET: /ExampleModel/
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

        //
        // GET: /ExampleModel/Details/5
        public ActionResult Details(Int32 SiteUID,Int32 UID)
        {
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        //
        // GET: /ExampleModel/Create
        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /ExampleModel/Create
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(Int32 SiteUID ,Int32 UID ,String ID ,String Name ,Decimal Turnover ,String StreetAddress ,String City ,String PostalCode ,String Telephone1 ,String Telephone2 ,String Email ,String Fax ,String Notes ,String Capacity ,Int16 NumberOfMealsPerDay ,Decimal SellingPricePerMeal ,Decimal BudgetPricePerMeal ,String BestTimeToCall ,Byte TradingDaysPerWeek ,Byte TradingWeeksPerYear ,Boolean AccountActive ,Int32 PrimaryContact_SiteUID ,Int32 PrimaryContactUID ,DateTime LastCallStartTime ,Boolean IsProfileCall )
        {
            if (ViewData.ModelState.IsValid)
            {
                // Attempt to add the user

                if (ExampleModel.Create(new {SiteUID ,UID ,ID ,Name ,Turnover ,StreetAddress ,City ,PostalCode ,Telephone1 ,Telephone2 ,Email ,Fax ,Notes ,Capacity ,NumberOfMealsPerDay ,SellingPricePerMeal ,BudgetPricePerMeal ,BestTimeToCall ,TradingDaysPerWeek ,TradingWeeksPerYear ,AccountActive ,PrimaryContact_SiteUID ,PrimaryContactUID ,LastCallStartTime ,IsProfileCall }, User.Identity.Name))
                {
                    TempData["StatusBar"] += "ExampleModel '" + ID + "' successfully added.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View();
        }

        //
        // GET: /ExampleModel/Edit/5
        public ActionResult Edit(Int32 SiteUID,Int32 UID)
        {
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        //
        // POST: /ExampleModel/Edit/5
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Int32 SiteUID ,Int32 UID ,String ID ,String Name ,Decimal Turnover ,String StreetAddress ,String City ,String PostalCode ,String Telephone1 ,String Telephone2 ,String Email ,String Fax ,String Notes ,String Capacity ,Int16 NumberOfMealsPerDay ,Decimal SellingPricePerMeal ,Decimal BudgetPricePerMeal ,String BestTimeToCall ,Byte TradingDaysPerWeek ,Byte TradingWeeksPerYear ,Boolean AccountActive ,Int32 PrimaryContact_SiteUID ,Int32 PrimaryContactUID ,DateTime LastCallStartTime ,Boolean IsProfileCall )
        {
            if (ViewData.ModelState.IsValid)
            {
                if (ExampleModel.Update(new {SiteUID,UID}, new {SiteUID ,UID ,ID ,Name ,Turnover ,StreetAddress ,City ,PostalCode ,Telephone1 ,Telephone2 ,Email ,Fax ,Notes ,Capacity ,NumberOfMealsPerDay ,SellingPricePerMeal ,BudgetPricePerMeal ,BestTimeToCall ,TradingDaysPerWeek ,TradingWeeksPerYear ,AccountActive ,PrimaryContact_SiteUID ,PrimaryContactUID ,LastCallStartTime ,IsProfileCall }, User.Identity.Name))
                {
                    TempData["StatusBar"] += "ExampleModel '" + ID + "' successfully updated.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View(ExampleModel.GetModelByPrimaryKey(new {SiteUID,UID}));
        }

        public ActionResult Delete(Int32 SiteUID,Int32 UID)
        {
            if (ExampleModel.Delete(new {SiteUID,UID}))
            {
                TempData["StatusBar"] += "ExampleModel successfully deleted.";
            }
            else
            {
                TempData["StatusBar"] += "ExampleModel delete operation failed!.";
            }
            return RedirectToAction("List");
        }

        public ViewResult List()
        {
            return View();
        }

        public ViewResult Grid(string RowClickEventHandlerFunctionName)
        {
            ViewData["RowClickEventHandlerFunctionName"] = RowClickEventHandlerFunctionName;
            return View();
        }

        public JsonResult GridData()
        {
            int totalOjectCount;
            var ExampleModelsList = EmExtJSGridFilterHelper.GetResults(Request, DataMan.ObjectContext.ExampleModel, out totalOjectCount);
            var list = new ArrayList();
            foreach (var ExampleModel in ExampleModelsList) //populate data containers with read data
            {
                list.Add(new
                             {
                                 ExampleModel.SiteUID ,ExampleModel.UID ,ExampleModel.ID ,ExampleModel.Name ,ExampleModel.Turnover ,ExampleModel.StreetAddress ,ExampleModel.City ,ExampleModel.PostalCode ,ExampleModel.Telephone1 ,ExampleModel.Telephone2 ,ExampleModel.Email ,ExampleModel.Fax ,ExampleModel.Notes ,ExampleModel.Capacity ,ExampleModel.NumberOfMealsPerDay ,ExampleModel.SellingPricePerMeal ,ExampleModel.BudgetPricePerMeal ,ExampleModel.BestTimeToCall ,ExampleModel.TradingDaysPerWeek ,ExampleModel.TradingWeeksPerYear ,ExampleModel.AccountActive ,ExampleModel.PrimaryContact_SiteUID ,ExampleModel.PrimaryContactUID ,ExampleModel.LastCallStartTime ,ExampleModel.IsProfileCall
                             });
            }
            return Json(new {dataitExampleInc = list.ToArray(), totalItExampleInc = totalOjectCount});
        }
    }
}

T4 Template used to Generate the Controller

<#@ template language="C#" HostSpecific="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="System.Data.Linq" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Data.Objects.DataClasses" #>
<#@ import namespace="System.Data.Linq.Mapping" #>
<#
MvcTextTemplateHost mvcHost = (MvcTextTemplateHost)(Host);
#>
<#
Type controllerType = Assembly.LoadFile("D:\\work_items\\ExampleInc\\ExampleInc-Alt-21\\ExampleInc.Models\\bin\\Debug\\ExampleInc.Models.dll").GetType("ExampleInc.Models." + mvcHost.ControllerRootName);

PropertyInfo&#91;&#93; piArray = controllerType.GetProperties(BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);

string actionMethodParamerters = "", anonTypePropValString = "", gridDataAnonTypePropValString = "";
foreach (PropertyInfo pi in piArray)
		{
			Type currentPropertyType = GetUnderlyingType(pi.PropertyType);

			if(IsBindableType(currentPropertyType) && IsNeededProp(pi.Name))
			{
				actionMethodParamerters += currentPropertyType.Name + " " + pi.Name + " ,";
				anonTypePropValString += pi.Name + " ,";
				gridDataAnonTypePropValString += mvcHost.ControllerRootName.ToLower() + "." + pi.Name + " ,";
			}
		}
actionMethodParamerters = actionMethodParamerters.Remove(actionMethodParamerters.Length - 1); //remove the last extra ","
anonTypePropValString = anonTypePropValString.Remove(anonTypePropValString.Length - 1); //remove the last extra ","
gridDataAnonTypePropValString = gridDataAnonTypePropValString.Remove(gridDataAnonTypePropValString.Length - 1); //remove the last extra ","
#>
using System;
using System.Collections;
using System.Web.Mvc;
using ExampleInc.Filters;
using ExampleInc.Lib.Helpers;
using ExampleInc.Models;
using ExampleInc.Models.UoW;

namespace <#= mvcHost.Namespace #>
{
	[Authorize]
    [NavigationFilter]
    public class <#= mvcHost.ControllerName #> : Controller
    {
        //
        // GET: /<#= mvcHost.ControllerRootName #>/
        public ActionResult Index()
        {
            return RedirectToAction("List");
        }

<#
if(mvcHost.AddActionMethods) {
#>
        //
        // GET: /<#= mvcHost.ControllerRootName #>/Details/5
        public ActionResult Details(<#= GetMethodParamertersString(controllerType) #>)
        {
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        //
        // GET: /<#= mvcHost.ControllerRootName #>/Create
        public ActionResult Create()
        {
            return View();
        } 

        //
        // POST: /<#= mvcHost.ControllerRootName #>/Create
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(<#= actionMethodParamerters #>)
        {
            if (ViewData.ModelState.IsValid)
            {
                // Attempt to add the user

                if (<#= mvcHost.ControllerRootName #>.Create(new {<#= anonTypePropValString #>}, User.Identity.Name))
                {
                    TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> '" + ID + "' successfully added.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View();
        }

        //
        // GET: /<#= mvcHost.ControllerRootName #>/Edit/5
        public ActionResult Edit(<#= GetMethodParamertersString(controllerType) #>)
        {
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        //
        // POST: /<#= mvcHost.ControllerRootName #>/Edit/5
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(<#= actionMethodParamerters #>)
        {
            if (ViewData.ModelState.IsValid)
            {
                if (<#= mvcHost.ControllerRootName #>.Update(new {<#= GetDataAnonTypePropValString(controllerType) #>}, new {<#= anonTypePropValString #>}, User.Identity.Name))
                {
                    TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> '" + ID + "' successfully updated.";
                    return RedirectToAction("List");
                }

                ModelState.AddModelError("_FORM", "Error");
            }

            // If we got this far, something failed, redisplay form
            return View(<#= mvcHost.ControllerRootName #>.GetModelByPrimaryKey(new {<#= GetDataAnonTypePropValString(controllerType) #>}));
        }

        public ActionResult Delete(<#= GetMethodParamertersString(controllerType) #>)
        {
            if (<#= mvcHost.ControllerRootName #>.Delete(new {<#= GetDataAnonTypePropValString(controllerType) #>}))
            {
                TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> successfully deleted.";
            }
            else
            {
                TempData["StatusBar"] += "<#= mvcHost.ControllerRootName #> delete operation failed!.";
            }
            return RedirectToAction("List");
        }

        public ViewResult List()
        {
            return View();
        }

        public ViewResult Grid(string RowClickEventHandlerFunctionName)
        {
            ViewData["RowClickEventHandlerFunctionName"] = RowClickEventHandlerFunctionName;
            return View();
        }

        public JsonResult GridData()
        {
            int totalOjectCount;
            var <#= mvcHost.ControllerRootName.ToLower() #>sList = EmExtJSGridFilterHelper.GetResults(Request, DataMan.ObjectContext.<#= mvcHost.ControllerRootName #>, out totalOjectCount);
            var list = new ArrayList();
            foreach (var <#= mvcHost.ControllerRootName.ToLower() #> in <#= mvcHost.ControllerRootName.ToLower() #>sList) //populate data containers with read data
            {
                list.Add(new
                             {
                                 <#= gridDataAnonTypePropValString #>
                             });
            }
            return Json(new {dataitems = list.ToArray(), totalItems = totalOjectCount});
        }
<#
}
#>
    }
}
<#+
public static List<PropertyInfo> GetEntityKeyProperties(Type type)
{
	List<PropertyInfo> keyProperties = new List<PropertyInfo>();

	PropertyInfo[] properties = type.GetProperties();

	foreach (PropertyInfo pi in properties)
	{
		System.Object[] attributes = pi.GetCustomAttributes(true);

		foreach (object attribute in attributes)
		{
			if (attribute is EdmScalarPropertyAttribute)
			{
				if ((attribute as EdmScalarPropertyAttribute).EntityKeyProperty == true)
				{
					keyProperties.Add(pi);
				}
			} else if(attribute is ColumnAttribute) {
				if ((attribute as ColumnAttribute).IsPrimaryKey == true)
				{
					keyProperties.Add(pi);
				}
			}
		}
	}

	return keyProperties;
}

public bool IsBindableType(Type type)
{
	bool isBindable = false;

	if (type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)) || type.Equals(typeof(decimal)) || type.Equals(typeof(Guid)) || type.Equals(typeof(DateTimeOffset)) || type.Equals(typeof(TimeSpan)))
	{
		isBindable = true;
	}

	return isBindable;
}

public bool IsNeededProp(string propName)
        {
            switch (propName)
            {
                case "CreationDate":
                case "LastModifiedDate":
                case "LastModifiedBy":
                case "EffectiveDate":
                case "ExpiryDate":
                case "ServerEntryDate":
                    return false;
            }
            return true;
        }

public string GetMethodParamertersString(Type viewDataType)
        {
			List<PropertyInfo> primaryKeys = GetEntityKeyProperties(viewDataType);

			if(primaryKeys.Count > 0) {
				string result = "";
				foreach(PropertyInfo pk in primaryKeys)
				{
					result += String.Format("{0} {1},", GetUnderlyingType(pk.PropertyType).Name , pk.Name);
				}

				return result.Remove(result.Length - 1); //remove the last extra ","
			} else {
				return "UID=Model.PrimaryKey";
			}
        }

public string GetDataAnonTypePropValString(Type viewDataType)
        {
			List<PropertyInfo> primaryKeys = GetEntityKeyProperties(viewDataType);

			if(primaryKeys.Count > 0) {
				string result = "";
				foreach(PropertyInfo pk in primaryKeys)
				{
					result += String.Format("{0},", pk.Name);
				}

				return result.Remove(result.Length - 1); //remove the last extra ","
			} else {
				return "UID=Model.PrimaryKey";
			}
        }

public Type GetUnderlyingType(Type type)
{
	Type currentPropertyType = type;
			Type currentUnderlyingType = System.Nullable.GetUnderlyingType(currentPropertyType);
			if(currentUnderlyingType != null) {
				currentPropertyType = currentUnderlyingType;
			}

			return currentPropertyType;
}
#>

hope this will help someone/myself in the feuture 🙂

April 27, 2009

Automate adding/using of the SVN revision to/as the version of a .net project, through a build target

Filed under: Uncategorized — Tags: , , — yasi8h @ 11:16 am

a bit too complex title eh? sorry about that. i am finally blogging about something after a long while. its a good idea to use the revision number of your source tree in thee release versions of your software. because it will enable you to ‘connect and identify’ your release versions with your source tree’s different revisions. this is how i went about ‘automating’ it. note that I use svn as my source control system, and VS(2008) as my IDE at work.

What we are trying to do

Read the AssemblyInfo.cs file in a project and replace the(/a part of the) version number for that project with it’s svn revision number.

What you need

Code

<!-- Import of the MSBuildCommunityTask targets -->
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

  <!-- to AssemblyInfo to include svn revision number -->
<Target Name="BeforeBuild">
	<SvnVersion LocalPath="$(MSBuildProjectDirectory)" ToolPath="$(ProgramFiles)\VisualSVN\bin">
	   <Output TaskParameter="Revision" PropertyName="Revision" />
        </SvnVersion>

	<FileUpdate Files="Properties\AssemblyInfo.cs"
                Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
                ReplacementText="$1.$2.$3.$(Revision)" />
</Target>

Steps

  • Locate and open your project file. ex:- exampleProject.csproj in a editor.
  • Edit(SvnVersion node’s ToolPath attribute) the path of the svn bin folder, where you find the executables(ex: svn.exe, svnversion.exe…).
  • go the very end and add the following code to the file. be sure to add this code before the </project> tag closing. so that your code is still inside the <project> node.
  • Save the exampleProject.csproj file.
  • it’s done! now every time you build your project, it’s AssemblyInfo.cs file’s version will contain the svn revision number as it’s last part(x.x.x.svn-revison)

How

Let me start by saying that I am no expert in MSBuild(reffer MSDN for more info). MSBuild is the build system used by VS to build your projects. when you create a project file it is basically a MSBuild file. the build file tell MSBuild how your project should be build/compiled and about any other actions that needs to taken to insure a succesful build. when we needed to include the svn revision number of our project in the AssemblyInfo.cs file we simply added a build target called AssenblyInfo. inside that we uses two MSBuild CommunityTasks(“The MSBuild Community Tasks Project is an open source project for MSBuild tasks. The goal of the project is to provide a collection of open source tasks for MSBuild.“).

  • SvnVersion – “Summarize the local revision(s) of a working copy.” – we use this to get the svn revision of our project. the result(revision) is stroed in a property called Revision.
  • FileUpdate – “Replace text in file(s) using a Regular Expression.” – we use this task to replace the revision portion of the version string(x.x.x.revision) with the content of the Revision property.

and that’s it. pretty simple eh!

References

March 19, 2009

Enum.GetValues on .net compact framework

Filed under: Uncategorized — Tags: , , — yasi8h @ 8:25 am

i recently needed to use Enum.GetValues on a .netcf project. but unfortunately it is not there in .netcf. googling gives a lot of solutions that we can use in .netcf. but i couldn’t find one that worked exactly as Enum.GetValue did.

Retrieves an array of the values of the constants in a specified enumeration.

is the method description for the Enum.GetValue, taken from msdn.com. most of the solutions online returned only an array of the constants. not their values. so i came up with the following. now of course this is inspired from lot of smart people’s code that i took from around the net sometime back. 😉

private static string EnumValuesToString(object e)
{
var eVal = (int) e;
var allValues = GetValues(typeof (Testnum));
var strings = new List();

foreach (var value in allValues)
{
if (value > eVal)
{
break;
}
if ((eVal & value) != value)
{
continue;
}
strings.Add(value.ToString());
}

return string.Join(“,”, strings.ToArray());
}

private static int[] GetValues(Type enumType)
{
if (enumType.BaseType ==
typeof (Enum))
{
//get the public static fields (members of the enum)
var fi = enumType.GetFields(BindingFlags.Static | BindingFlags.Public);
//create a new enum array
var values = new int[fi.Length];
//populate with the values
for (var iEnum = 0; iEnum < fi.Length; iEnum++) { values[iEnum] = (int) fi[iEnum].GetValue(null); } //return the array return values; } //the type supplied does not derive from enum throw new ArgumentException("enumType parameter is not a System.Enum"); } [/sourcecode] usage would go like [sourcecode language='csharp'] [Flags] public enum Testenum { fld1 = 1, fld2 = 2, fld3 = 4, fld4 = 8, fld5 = 16 } #endregion public ClassTest() { EnumValuesToString(Testenum.fld1 | Testenum.fld2 | Testenum.fld4); } [/sourcecode] may this help me/you/him or her in the future 🙂

March 5, 2009

Rails ActiveRecord: Having Multiple Relationships within Two Models

Filed under: Uncategorized — Tags: , , , , — yasi8h @ 3:03 am

noob alert!: I am pretty new to RoR. so i might not be solving the problem with the best possible solution.

Scenario 1: many-to-many + one-to-many

Task: I have two models. Contact and ContactSource. a Contact belongs to a ContactSource. and a Contact can be synced with one or more ContactSource(s). i need to get the ContactSource(s) with whom a given Contact is synced ex:- Contact1.synced_with => [ContactSource1, ContactSource2…]

Solution:  for the first relationship(one-to-many) we can use belongs_to. and for second we can use has_many with :through

class Contact :contacts_synced_with_contact_sources

end

class ContactsSyncedWithContactSource < ActiveRecord::Base belongs_to :contact belongs_to :contact_source end [/sourcecode] In the above code we have no problem accessing the data relevant to the two relationships. because: Contact.contact_source would point to the Contact Source who owns the Contact while Contact.contact_sources would point to the ContactSources with whom the Contact is synced with. but look at the next problem.... Scenario 2: many-to-many + many-to-many Task: I have two models. Book and Student. a student can own many books, while a book(this does not refer to a individual copy of a book, but to all the copies of a book) can be owned by many students. and a student have a collection of books that he loves. Problem: here we have two many to many relationships within two business entities. if you try to solve this with has_many :through or has_and_belongs_to_many, you would run in to a problem. in the first problem you referred to the ContactSources by using the property accessor contact_sources. but here if you try using student1.books how would ActiveRecord know what to return?(book the student love or the books he own?). you could solve this problem by using the :source option(is that what you call these in ruby? :P) in has_many... it would like this... has_many :books_i_love, :source => “book”, :through => :student_love_books

you can give a custom property accessor name such as books_i_love but then you have to use the :source => “book” to let rails know about what model(s) you are trying to access.

if you do not use the :source => “book” you would get a very helpful error message like this

ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :books_i_love or :books_i_love in model StudentLoveBook. Try 'has_many :books_i_love, :through => :student_love_books, :source => '. Is it one of :student or :book?

source follows…

class Student “book”, :through => :student_love_books

has_many :student_own_books
has_many :books, :through => :student_own_books
end

class Book < ActiveRecord::Base end class StudentLoveBook < ActiveRecord::Base belongs_to :student belongs_to :book end class StudentLoveBook < ActiveRecord::Base belongs_to :student belongs_to :book end class CreateBooks < ActiveRecord::Migration def self.up create_table :books do |t| t.string :name t.timestamps end end def self.down drop_table :books end end class CreateStudents < ActiveRecord::Migration def self.up create_table :students do |t| t.string :name t.timestamps end end def self.down drop_table :students end end class CreateStudentOwnBooks < ActiveRecord::Migration def self.up create_table :student_own_books do |t| t.column :student_id, :integer t.column :book_id, :integer t.timestamps end end def self.down drop_table :student_own_books end end class CreateStudentOwnBooks < ActiveRecord::Migration def self.up create_table :student_own_books do |t| t.column :student_id, :integer t.column :book_id, :integer t.timestamps end end def self.down drop_table :student_own_books end end [/sourcecode] hope this would help someone/myself in the future 🙂

Older Posts »

Create a free website or blog at WordPress.com.