Tuesday, September 7, 2010
Getting Rid of the Form Helper Hidden Checkboxes
For someone who's used the form helper in Monorail and can't seem to find a way to get rid of the extra hidden field that is created (so that you can use the true and false value), I found a dictionary entry hidden in the source that gets rid of it. Simply set suppressHiddenField to true and the hidden field will no longer show up!
Tuesday, August 31, 2010
Broken AspView Sending Emails
One more broken thing I've noticed and had to fix in the source - you cannot send emails when using a layout (non-deprecated methods) from monorail. In order to fix this, I had to change one line in the asp view source. In AspViewEngine.Process(string, string, TextWriter, IDictionary):
The problem was that the context being passed into the next process step was null, and I was getting a null object ref when trying to send an email. With this fix in place, everything works as expected. Not sure if this is an oversight on the AspView creator's part or just an odd chain of events.
Process(templateName, output, null, null, controllerContext);changes to
Process(templateName, output, EngineContextLocator.Instance.LocateCurrentContext(), null, controllerContext);
The problem was that the context being passed into the next process step was null, and I was getting a null object ref when trying to send an email. With this fix in place, everything works as expected. Not sure if this is an oversight on the AspView creator's part or just an odd chain of events.
Castle Monorail/Activerecord on Mono
I have had a heck of time trying to get Monorail working fully on Mono (2.4, 2.6, and even the latest tarballs). After weeks of sorting through code, I think I have finally figured out the solutions to the most common problems I've encountered:
1) Invalid casts in the form helper select generation. This has taken me quite awhile to track down, and the solution is simple enough that I thought I would blog about it to remember what I done in the future. The problem is with casting and the proxies for AR objects. The AR objects that are proxies, when used in a form helper select can sometimes generate an InvalidCastException if they have already been used. I ended up getting the source from GitHub and modifying the form helper itself in the ReflectionValueGetter. Here is the full GetValue method I used in the AbstractFormHelper.cs:
The new code is in the extra try/catch InvalidCastException block. It merely gets the base type and tries to find the property there if it was an invalid cast (ie Instrument being cast as InstrumentProxy). More can be found here.
2) Asynchronous actions do not work in mono (at least using the AspView engine and the ActiveRecord integration). I finally tracked this down due to the incorrect or null context being assigned on the End method. Luckily, the fix is VERY easy, especially considering how long it took me to track down. Merely do "HttpContext.Current = HttpContext" in the end method of the action, and it will correctly release the ActiveRecord session in the web module or in your custom methods to attach/release the session. More here.
1) Invalid casts in the form helper select generation. This has taken me quite awhile to track down, and the solution is simple enough that I thought I would blog about it to remember what I done in the future. The problem is with casting and the proxies for AR objects. The AR objects that are proxies, when used in a form helper select can sometimes generate an InvalidCastException if they have already been used. I ended up getting the source from GitHub and modifying the form helper itself in the ReflectionValueGetter. Here is the full GetValue method I used in the AbstractFormHelper.cs:
public override object GetValue(object instance) { try { try { return propInfo.GetValue(instance, null); } catch (InvalidCastException) { propInfo = propInfo.ReflectedType.BaseType.GetProperty(Name); return propInfo.GetValue(instance, null); } } catch (TargetException) { var tempProp = instance.GetType().GetProperty(Name); if (tempProp == null) { throw; } return tempProp.GetValue(instance, null); } }
The new code is in the extra try/catch InvalidCastException block. It merely gets the base type and tries to find the property there if it was an invalid cast (ie Instrument being cast as InstrumentProxy). More can be found here.
2) Asynchronous actions do not work in mono (at least using the AspView engine and the ActiveRecord integration). I finally tracked this down due to the incorrect or null context being assigned on the End method. Luckily, the fix is VERY easy, especially considering how long it took me to track down. Merely do "HttpContext.Current = HttpContext" in the end method of the action, and it will correctly release the ActiveRecord session in the web module or in your custom methods to attach/release the session. More here.
Tuesday, May 25, 2010
ValidateIsUnique on Monorail
I have been playing with Castle Monorail quite a bit, and am currently working on building a large administration project using it. The problem I've run across recently is with validating models when using ARDataBind (or the DataBinder in general). Even though I was using the recommended code:
But I found that I was still getting an invalid object when the object was being saved, or in other words: the HasValidationError method wasn't catching invalid objects. I have beating my head against the wall many a time until today when I realized something - the ValidateIsUnique attribute comes from Castle.Activerecord and NOT from Castle.Components.Validator. Sure enough, I realized that any problems I had with HasValidationError not catching an invalid object came when I used ValidateIsUnique.
So after digging through some code for a couple of hours, I haven't quite found a solution that I like. I may post on Castle Project mailing list to see if I get some answers there. As of right now I believe the only way to really validate and catch this attribute if the validation failed is to include a separate catch block after the HasValidationError block:
if (HasValidationError(model)) { Flash["summary"] = GetErrorSummary(model); RedirectToReferrer(); return; }
But I found that I was still getting an invalid object when the object was being saved, or in other words: the HasValidationError method wasn't catching invalid objects. I have beating my head against the wall many a time until today when I realized something - the ValidateIsUnique attribute comes from Castle.Activerecord and NOT from Castle.Components.Validator. Sure enough, I realized that any problems I had with HasValidationError not catching an invalid object came when I used ValidateIsUnique.
So after digging through some code for a couple of hours, I haven't quite found a solution that I like. I may post on Castle Project mailing list to see if I get some answers there. As of right now I believe the only way to really validate and catch this attribute if the validation failed is to include a separate catch block after the HasValidationError block:
if (!model.IsValid()) { ActiveRecordMediator.Evict(model); Flash["message"] = String.Join(",", model.ValidationErrorMessages); RedirectToReferrer(); return; }
Tuesday, April 20, 2010
ActiveRecord Lazy Initialization
I enabled lazy initialization by using the [Lazy=true] attribute on my Castle ActiveRecord classes, but I noticed that my application was being severely slowed down (by 30 seconds or so) when using a PaginationHelper combined with a FindAll on the class. I figured that lazy initialization on the activerecord class would mean that none of the objects would be fully loaded except for the ones that were presented in the paginated view, which was 20. Well, it turns out that I was wrong because of a "feature" I didn't find documented in ActiveRecord.
Apparently, any collections that are done using at least the [HasMany] attribute in an active record class must be separately defined as lazy in order to get a truly lazy initialized class. As soon as I added Lazy=true to the HasMany attribute, I found that the pages loaded within a second or so, and problem solved! Just to be sure, I added Lazy=FetchWhen.OnInvoke to the reverse class with the [BelongsTo] attribute, but depending on the implementation this may not be needed.
Apparently, any collections that are done using at least the [HasMany] attribute in an active record class must be separately defined as lazy in order to get a truly lazy initialized class. As soon as I added Lazy=true to the HasMany attribute, I found that the pages loaded within a second or so, and problem solved! Just to be sure, I added Lazy=FetchWhen.OnInvoke to the reverse class with the [BelongsTo] attribute, but depending on the implementation this may not be needed.
Thursday, March 25, 2010
Installing Redmine on Ubuntu 9.10
A week or so ago, I installed Redmine to use for project management for a team of developers and for myself on other projects. It took awhile and some gathering from different sources to be able to actually get it installed and to be able to use the passenger module for Apache so that it would run Ruby-on-Rails. I'm not that familiar with Ruby, so this may be common sense, but I figured I would write up what I did for an Ubuntu installation that hasn't been using Ruby at all.
Install prereqs (Make sure you are root or just use sudo):
Download and extract Redmine latest stable (0.9.3 at the moment)
Comparing the table at the Redmine site, I saw that I needed rails version 2.3.5, so install that through gem. This is probably where I spent the most time as I kept trying to use the Ubuntu provided packages of rake and rails, but just installing gem and then installing them through that finally worked correctly and that's what I would recommend doing. Here are the packages to install with gem:
Now to actually configure Redmine to use the database, here I use MySQL, but it's easy to change. This is also assuming that you installed (extracted) it into /var/www/redmine:
Here is my configuration for the database (password blanked for obvious reasons, use your own here):
Install prereqs (Make sure you are root or just use sudo):
apt-get install ruby rubygems ruby1.8-dev libgemplugin-ruby libgemplugin-ruby1.8 apt-get install libruby-extras libruby1.8-extras rubygems1.8
Download and extract Redmine latest stable (0.9.3 at the moment)
Comparing the table at the Redmine site, I saw that I needed rails version 2.3.5, so install that through gem. This is probably where I spent the most time as I kept trying to use the Ubuntu provided packages of rake and rails, but just installing gem and then installing them through that finally worked correctly and that's what I would recommend doing. Here are the packages to install with gem:
gem install rails -v=2.3.5 gem install rake gem install mysql gem install passenger
Now to actually configure Redmine to use the database, here I use MySQL, but it's easy to change. This is also assuming that you installed (extracted) it into /var/www/redmine:
cd /var/www/redmine cp config/database.yml.example config/database.yml vi config/database.yml
Here is my configuration for the database (password blanked for obvious reasons, use your own here):
production: adapter: mysql database: redmine host: localhost username: redmine password: ********* encoding: utf8
Now to actually create the database, first log into mysql and run these commands (again changing the password to your own):
create database redmine character set utf8; grant all on redmine.* to 'redmine'@'localhost' identified by '********';
Finally, we are ready to start configuring Redmine itself. The biggest gotcha here is that rake isn't on the path (at least it wasn't in both of my installs). The easiest way is to just not worry about it, but you can put it on the path by making a symbolic link in the /usr/bin dir or somewhere if you want. I just used the full path. If you can't find the rake binary, try
find / -name "rake"
Once you find it, use this in place of /var/lib/gems/1.8/bin/rake if it is different for you. Make sure that you are in the Redmine directory (/var/www/redmine for me) again. The first of the next commands initialize a secret word that the rest of the process needs. After this default data is loaded and finally the default db data is loaded into the database you defined earlier:
/var/lib/gems/1.8/bin/rake config/initializers/session_store.rb RAILS_ENV=production /var/lib/gems/1.8/bin/rake redmine:load_default_data RAILS_ENV=production /var/lib/gems/1.8/bin/rake db:migrate RAILS_ENV=production
Next we move on to configuring passenger, which is an apache2 module that will run Ruby-on-Rails sites inside of virtual hosts or wherever you would like to put them. We will need to find the directory it was installed to first, then compile it, and finally enable it in apache itself. It should be in the same relative location as rake; for me it was in /var/lib/gems/1.8/gems/passenger-2.2.11/
cd /var/lib/gems/1.8/gems/passenger-2.2.11/ bin/passenger-install-apache2-module
Make sure that everything compiles fine, and now we will need to activate it in apache itself. Go to the mods-available directory in the apache2 folder (/etc/apache2/mods-available), and create a file called passenger.load with the following line (note this may change if your path to passenger was different):
LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.11/ext/apache2/mod_passenger.so
Next create another file called passenger.conf and add these lines to it changing for your own path if needed:
PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.11 PassengerRuby /usr/bin/ruby1.8
Finally enable the module in apache2:
a2enmod passenger
The next step is to create either a virtual host or use symbolic links and such to create a new directory entry in an existing site for Redmine (which I did). I will post the directory directive here, but it should be trivial to incorporate this into a new virtual host:
<Directory "/var/www/site_path/redmine"> Options -Indexes FollowSymLinks RailsEnv production RailsBaseURI /redmine AllowOverride All Order allow,deny Allow from all </Directory>
The thing to get from this is "RailsEnv production", which should match all the commands you've been giving the whole time. One thing caught me here the second time - no matter which method you use to host it, make sure that the Redmine host/directory is pointing to the public folder in the root redmine folder.
Finally, restart apache:
/etc/init.d/apache2 restart
Browse to the server page and enjoy! As a final note, the default user/password is admin/admin to get things started.
Monday, March 8, 2010
Getting Things Started
Although I've been programming for years, I still consider myself a beginner in many ways. I know I write spaghetti code, and for the most part I'm ok with that, but I always look to improve my techniques. In following with that idea, I have been working on projects in C# for about 6 months now. Again - spaghetti code. Recently, I (re)discovered MVC frameworks and considered recreating a few websites I have using a framework. I first looked at the PHP frameworks - CodeIgniter, Yii mainly - and then I realized the power of the Castle project.
As I've been messing with ActiveRecord, Monorail, and MicroKernel/Windsor, I've been impressed with what is possible with it, but also I'm disappointed in much of the documentation. It seems something that should be very simple, such as creating a new AspView page that creates a simple form, should be easy and no problem, but it ends up taking quite awhile because there is simply either no documentation or scattered documentation. I've been playing around with the idea of creating a blog like this for awhile, but decided to go ahead and because of these things, I've decided to go ahead and start it with the idea of documenting some of the things I've been doing in Monorail, etc. I hope that this can help some other people just starting out and can maybe help to get some undocumented/out of date documentation updated. More to follow soon...
Subscribe to:
Posts (Atom)