|
LATEST POSTS
Tuesday, December 11, 2007
Have started a new project at the current contract and I'm muy excited about it. It's a "legacy" .NET 1.1 app that was upgraded to 2.0 by running the wizard, then left alone. It has a web service created by an architect who appears to have worked with Jeremy Miller before. It returns datasets. Lots of them. There's a DataDude project created in some CTP version that can't be upgraded. There is no CI process. I discovered most of this without cracking open the code. I have since done so and analogously speaking, it is clearly the result of the type of relationship we hillbillies are *very* familiar with. I'm pretty pumped to dive into it, actually. The CI process is mostly done. The tests are running regularly (2.5% code coverage, woohoo!). Now it's time to start the grand-scale refactoring. First, however, we need to go to the customer and tell him, "The architecture of this app is not unlike something akin to roadkill. It compiles only during the waxing phase of the moon and the fact that it runs at all has restored my faith in God. I know you've been using it relatively successfully for several months but we'll need several tens of thousands of dollars from you to refactor large portions of it. When we're done, there will be no discernible difference to the application from your perspective but we in the IT department will sure feel better about ourselves." Now I've never been able to say "discernible" out loud convincingly and I'm sure as heck not going to imply that I believe in a higher power so obviously, this isn't going to work for me. Which leads me to a tactic I've become quite fond of: Guerrilla Refactoring Guerrilla refactoring requires some unconventional tactics. Like in guerrilla warfare, I'll need to ambush the code often. Come at it from an angle it's not expecting. Do quick, focused skirmishes and get out again before anyone notices (except the code which will be grateful to be liberated from its bourgeois oppressors). Subterfuge and guile will be my allies. "How long will it take to implement the 'sync' function? Four hours, minimum. Change the layout of the form? A good day of coding. You need the text changed on this label? I'd say....three full days. Not including writing tests to expose the bug." I will bear the brunt of their skeptical, leering eyes for the sake of the common good. No longer will we toil under the totalitarian regime of logic-laden views and hand-rolled data access code. Their armies will *tremble* in fear in the face of my legion of Freedom Fighter Presenters! And the fascists will COWER as I slash at them with my Sword of NHibernate!! JOIN ME, brothers (and to a lesser extent, sisters), as we stamp out the Dictatorship of Duplication and drive a *hammer* through the very heart of hardcodedness!!1!!1!!!! VIVA LA REVOLUCION!!!! Kyle the Pacifist
Tuesday, October 30, 2007
Oy vay, the hillbilly has to be more careful adding Part 1 to the beginning of a post. Too many implications about his commitment involved. This is a follow up to a post I did to help guide new MonoRailers through the Timok sample application. If you're just joining us, or have hit me from a random search engine (oh, let's just say it: Google), the intent is to help get up to speed with MonoRail by explaining the steps one might go through when looking through a sample application. I haven't been diving too deeply into the topics because I'm not really a deep-diver by nature, unless it is required. Scared I might get the coding equivalent of the bends. A friendly reminder/disclosure/EULA that, for the most part, I'll avoid any "I think"s and "It looks like"s and "My guess is"es for the sake of readability. Consider this your unqualified qualification that anything I say here is the result of my own investigations with a liberal helping of my previous web experience. When we left off, we had gone through how MonoRail routes requests to an appropriate controller and how that controller determines which HTML to render on the page. We walked through a request to /home/index.rails which led us to the default.vm layout in the Views/layouts folder and the index.vm view in the Views/Home folder in Timok.Rbr.Portal.Web. index.vm contains the HTML that will be rendered within the default.vm layout (akin to a "master page"). But before we div---er, wade into index.vm, we need to back up a bit. I tried to navigate to /home/index.rails but was redirected to a login page. WHAT GIVES?!? Open up HomeController.cs again and notice that it derives from SecureController, which in turn derives from ControllerBase, which in turn derives from SmartDispatcherController. SmartDispatcherController is the main MonoRail controller class and all your controllers must derive from it (or its own base class, Castle.MonoRail.Framework.Controller, but that isn't as common). It's common for MonoRail applications to create their own ControllerBase derivation, similar to creating your own PageBase in traditional web forms. In this case, we also further derive a SecureController class which, as its name implies, allows you to secure access to any controllers that derive from it. How does SecureController restrict access to controllers? The class has the following attributes: [Filter(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=1)]
This tells MonoRail that before any action can be performed on this controller, we need to first process a filter of type AuthenticationFilter. ExecutionOrder is helpful when more than one filter is applied to a controller, which isn't the case here.
Note that the controllers.config file contains an entry for our AuthenticationFilter class. While the above attribute indicates that we have to use a class of type AuthenticationFilter for the filter, it doesn't say which class specifically needs to process the request. This is another example of how MonoRail uses Windsor, the IoC container. By registering AuthenticationFilter in the controllers.config file, we tell MonoRail "if you need a class of type AuthenticationFilter somewhere and one hasn't been specifically requested, use this one".
OK, fine, so how does *AuthenticationFilter* restrict access to controllers then?
I'm not sure I like your tone, kiddo. Maybe I should just leave you to figure this out on your own, yesno?
No, no, no. I didn't mean it like that. I apologize. Please continue.
That's better. AuthenticationFilter (which derives from MonoRail's Filter class) overrides OnBeforeAction. This code will be executed before any action is performed on a controller that uses this filter. And there isn't anything particularly MonoRail-ish in this code. It checks to see if users are logged in using plain old System.Web.Security.FormsAuthentication. If they aren't, they are redirected to the login page. Or rather, they are redirected to the Index action of the Login controller.
If they are authenticated, OnBeforeAction returns true, which means the request will filter through to the originally requested action (in our case, the Index action on the Home controller). The last line of OnBeforeAction says "return false". The code never makes it this far because it either returns true or redirects to the login page before it gets here. But if it did get to this line and returned false, this tells the controller using this filter that it didn't "pass the test". The controller will then display a blank page which you can verify by commenting out both SendToLoginPage lines.
Summary
Two posts and we still haven't actually rendered anything yet. Yeah, well, security has a tendency of grinding everything to a halt. Here we discussed:
- Applying filters to controllers to execute code before an action is processed
- Deriving your own controller hierarchy
- Using Windsor again to provide a class when one isn't specifically requested
- Being polite to the hillbilly
Next up: We'll finally look at some rendered code and, with luck, what happens when you do something on the page.
Kyle the Insecure
Saturday, October 20, 2007
In my latest effort to pad out my posting count, I'm going to take a sample MonoRail application and annotate it. The reason being: I would have liked one myself. Yes, I can download samples and try to grok it by flipping back and forth 'twixt code and the API Reference Manual. But when I'm looking at a sample app, there are a lot of times I ask "why are you doing it this way?". And there are times where I just plain don't feel like slogging through documentation looking for the section that applies to a specific piece of code. That's about all I'm going to do by way of explanation because this will probably be a long enough post as it is. The sample app I'm going with is the Timok Portal Project prepared by the Castle folks themselves. I considered also the source code for the ALT.NET conference website (or at least a version of it) but frankly, I'm a little scared to say ALT.NET these days for fear that the Pedantic Police will take me away. This little adventure will assume little knowledge of MonoRail other than what it does conceptually. It also assumes you are familiar with traditional web forms and trying to map this new world into what you know. It's laid out as if you are first opening the app and trying to navigate your way through the flow starting from default.aspx. So I will jump around from topic to topic but it's meant to answer questions as they would theoretically occur chronologically. Whenever there are no references to explicit explanations, it is essentially my understanding of how things work. So take it as such. So without further adieu, we'll see how far we get today... Default.aspx Your standard MonoRail default page. It does nothing other than redirect you to the "real" main page: /home/index.rails. It exists because very likely, the IIS directory is configured so that default.aspx is the default page. OK, so let's take a look at index.rails. Waitaminute... It doesn't exist. And in fact, you may also be looking for a home directory off the root. Stop thinking of things in terms of files and directories. If you look in the web.config, there is a section called <httpHandlers>. The first one in the list says that any time ASP.NET finds a URL request that ends in .rails (excluding the query string), process that request with the MonoRailHttpHandlerFactory. That factory parses the URL and decides what to do with the request based on what it finds. And 99% of the time, that *doesn't* involve navigating to and processing an aspx file. To continue from default.aspx, the page redirects you to /home/index.rails. In MonoRail-speak, this means: Find the controller for "home" and process it using "index" as the action to perform. How does it know where the home controller is? Back to web.config. The <castle> section lists references to a bunch of other config files. One of these is controllers.config, which lists all the controllers used in the application. The one we are interested in is home.controller, which refers you to the HomeController class in the web project. How does it know to look at the "home.controller" element? Through the magic of convention and the Windsor IoC container. If you're not familiar with Windsor or IoC, think of it as a way of helping the application to find things it needs when they aren't explicitly provided to it. In this case, Windsor is helping the application to find which class to use when we request the home controller. In this case, it's HomeController.cs in the Controllers folder of the web app, as can be seen from the home.controller component. So where's the HTML that HomeController renders? It may not seem obvious but there are other files associated with this class, similar to how an aspx file is associated with its code-behind/code-beside file. The controller has two attributes attached to it: Layout("default") and Rescue("generalerror"). The Layout attribute tells us which layout to use when rendering the page. Layouts are stored in the layouts folder in the web project. This is always where MonoRail looks for layouts which is why there is no explicit link to the folder from the controller. Layouts are analogous to master pages and I'll leave it at that for now. If you open the layouts folder, there is only one layout: default.vm. Coincidentally, the same name as was used in the Layout attribute of the home controller. (Note: It's not really a coincidence.) So at this point, we have the controller telling us which layout to use to render the page template. Default.vm contains mostly HTML with some "other stuff" thrown in. The only "other stuff" I'm interested in at the moment is $childContent, which is analogous to the PlaceHolder ASP.NET server control in a master page. This is where the individual content goes for each controller that uses this layout. The HTML that goes into that placeholder is located under the Views folder in the web project. Specifically, it is in Views/Home/index.vm. Again, there is no direct link telling the controller to go to this file specifically. Rather, Monorail will look under the Views folder for a subfolder matching the name of the controller. And also note that the name of the file, index.vm, matches the name in the URL we are requesting: index.rails. Minus the extensions. Again, not a coincidence. One final note on the controller. It contains a method called Index which, by this time, should give you some indication that it is important with our request for index.rails. This method will be executed before the index.vm view is rendered. And again, nowhere does it explicitly say, "if you are requesting index.rails, execute the Index method in HomeController". The underlying link in all of this is just the name of the request: index. Summary In our request for /home/index.rails, the following actions were taken: - The request was routed to MonoRailHttpHandlerFactory because it ended in .rails
- The factory parsed the URL to determine which controller should be used to render the request. Using the Windsor container, it determined that we should use HomeController to render the request
- HomeController executed the Index() method, loaded the default.vm layout, and inserted the index.vm view into it and rendered the result.
One of the underlying themes you may have seen is the reliance on "convention over configuration". MonoRail knows where to find things because of the conventions used. Views are stored in the views folder, layouts in the layouts folder. The Index method is executed because we are responding to the index.rails request. Compare this with an aspx page where there is an explicit link 'twixt it and it's underlying code-behind file (via the Inherits attribute on the <@Page@> directive). This is (hopefully) the first in a series because, quite frankly, we haven't covered much yet. Next time, we'll continue where we left off, which is with the index.vm view. But before that, we'll need to talk about why, when you launch the application, you are actually first re-directed to the login page. Kyle the Derailed
Tuesday, July 31, 2007
Just finished Jean-Paul Boodhoo's Nothin' But .NET course and everything you've heard about it is true. This was, I believe, the first course where I was paying rapt attention and still got lost. It was a fleeting moment but it was glorious! It was like the restaurant scene in The Meaning of Life where my brain is Mr. Creosote and JP is the Maitre d' trying to cram one more wafer of knowledge into it.
There are at least a dozen blog potential posts scattered throughout the week but I'll limit my own coverage to one since I believe Master Simser will probably cover the actual content better than I could. So I'll let him deal with any confidentiality conflicts with JP and instead, I'll post more personal highlights
Day One
JP covers factory methods, state-based vs. interaction-based tests, MbUnit vs. NUnit, guard clauses, and delegates. It's 8:35am.
At 6pm, we lose a couple of people who favour family over brain-scrambling. JP is adamant that it's cool but there's rejection in his eyes and for the remaining three hours, he covers only thirty-six topics, down from his usual rate of fifty-nine per hour.
At the end of the day (a respectable 7:15ish), I decide that what JP really needs is a soundtrack. I vow to remedy this tomorrow.
Day Two
8:30AM - I initiate the course soundtrack with Also Sprach Zarathustra.
1:00PM - Afternoon musical inspiration: Kids In The Hall Theme
6:15PM - I concede defeat and stop trying to keep up on my own laptop.
7:03PM - He invites me up to implement a test. I GOT TO TOUCH JP'S KEYBOARD! See entry for day five.
9:15PM - JP claims he's raring to keep going but prefixes this statement with a half-hidden yawn. We let him off the hook...this time.
Day Three
Today's musical selections: Feelin' Groovy (take note of the opening lyrics), Delilah (by request), and Linus & Lucy
8:30AM - JP claims not to have gotten any sleep last night. Despite this, I switch from tea to a double double with a shot of espresso at Starbucks to try to get my brain vibrating on the same frequency. Also, Jolt. JP orders water.
11:00AM - We take a break while he rants about properties. Or methods or operators or something. Frankly, I'm just glad he's distracted enough that I can catch up.
7:15PM - He laments that we've missed a couple of topics so he refactors the app we're working on so quickly, we travel back in time a few hours so that we can get back on track.
2:35PM - Maintainability trumps all other abilities. It takes me a while to determine he is talking about code and not children.
6:45PM - At one point in my life, I was able to play Flight of the Bumblebee on the piano fairly respectably. I may as well have been playing chopsticks compared to what it takes to keep up on my laptop. Interestingly, Bumblebee is probably an accurate description of the colour of JP's laptop.
8:15PM - It occurs to me that ReSharper has so many keyboard shortcuts, you could conceivably finish your application by mashing on your keyboard at random.
Day Four
Morning inspiration: He Ain't Heavy (He's My Brother) (by request)
9:30AM - We finish implementing the dependencies on the domain. JP claims, "Now we can *really* start moving"
1:00PM - He starts playing his own music to code to. *NOW* we're talking. There is a minor quibble about the selection but they are quickly smacked down (oddly enough, with a ReSharper shortcut).
3:35PM - I thought the music would lull him a little but he actually appears to be speeding up. He no longer moves his hands to type. Instead his hands hover over the keyboard and the appropriate keys press themselves out of respect. Or, more likely, fear.
6:55PM - We settle in for a long night because JP claims he has to catch a flight tomorrow night at 7:30.
10:45PM - We wrap up and I check in with the missus back home in the Bahamas who accuses me of neglecting my daughter, cheating on her, and eating too much cholesterol-laden pizza. Take note, prospective students, Nothin' But .NET is bad for the home life. Even if half your family is two countries away.
Day Five
9:45AM - JP's laptop has just undergone some minor emergency surgery for the last hour and a bit but steadfastly refuses to boot up. We hold a small service and JP performs a rousing rendition of The Rose with the rest of the class backing him up in five-part harmony. The Bumblebee is down and I volunteer my laptop as a surrogate for the day. It is promptly pimped out with "only the minimum [I'll] need to work effectively". I'm left with 2Gb of hard drive space and much software that will expire in thirty days.
1:25PM - Now I get it! He's pre-recorded everything he's done and is playing back at four times the normal speed and only *pretending* to type. Don't worry JP, you're secret is safe with the Hillbilly.
5:30PM - "Oh yeah, I cancelled my flight tonight. Is everyone okay to stay a little longer?" I'm too scared to leave as every single thing he covers feels like my entire career depends on my knowing it.
9:00PM - I have to physically pry JP off my laptop. He breaks free of my grip but amid cries of "Stop him! Before he refactors again!" we manage to subdue him with a Bumblebee to the left temple.
I jest, of course (which I mention only because I've name-dropped JP so much that I'm afraid some humourless Googler will come hunting me down). The course was a delight as my dear grandpappy would say if he had all his teeth and could speak (and were still alive). Especially give my recent odyssey into self-improvement, career-wise at least. Having tried self-study for the last few months on many of the topics, it was great to have them explained in a structured, albeit rapid-fire manner and to have a context for the many patterns discussed as well as some nuggets about how some of them fare in the real-world. And there is absolutely no denying JP's passion for what he does which in and of itself elevates this above many similar courses.
The hard part now is going to be reining myself in. Not since the three-day recap of the Hillbilly's family tree has my brain been so abuzz with possible avenues of opportunity. It will be a case of holding a hammer and trying not to look at everything like it was a nail. We were hit with so much so fast, there is an overwhelming urge to apply as much as we can as soon as we can so the knowledge does come dripping out of our ears.
And in that respect, I'm planning to spend some quality time with the resulting code over the coming weeks, adding remaining functionality and refactoring existing stories. Perhaps integrating NHibernate or Windsor just to say I did.
But I'm not looking forward to all the cash I gotta start laying out when all this trial software expires....
Kyle the Supersaturated
Wednesday, April 04, 2007
OK, dear reader(s), I haven't had much luck engaging you in the past but let's see how I fare in 2007. I'm implementing my very first Model-View-Presenter. She's a bonny WinForms application that, at its core, takes an Excel spreadsheet and converts it into another format. It uses a plug-in architecture so that you (as in I) can create other "converters" and drop them into the plug-in directory without having to recompile. That has nothing to do with my question but I've typed it and I don't want to delete it. I've seen many, many, many, many examples of Model-View-Presenter, most of them very clear and easy to follow but all suffering from a problem I am just now encountering: My form does more than display a list of employees. So my question is, how do you implement a page that, for example, displays two lists, then allows the user to select one item from each and click a button that will do something with the two selections? Sub-questions: - Do you encapsulate each list in a user control, each implementing their own view and presenter?
- Do you create a view/presenter for each section and have the main form implement each view and create each presenter?
- Do you treat the whole form as one view with one presenter?
- Do you use multiple views (one for each "section") with one presenter to control them all?
- Do you use one view but register it with multiple presenters? (I threw that in for completeness; it sounds kind of wacky to me.)
The first person that responds with "It depends" will be shot on sight.
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent
my employer's view in any way.
Copyright © 2008 Kyle Baley. All rights reserved.
|
|
|
LATEST POSTS
POPULAR POSTS
LINKS
BLOG ROLL
|
|
CATEGORIES
ARCHIVE
| December, 2007 (8) |
| November, 2007 (8) |
| October, 2007 (23) |
| September, 2007 (15) |
| August, 2007 (8) |
| July, 2007 (6) |
| June, 2007 (11) |
| May, 2007 (19) |
| April, 2007 (14) |
| March, 2007 (3) |
| February, 2007 (4) |
| January, 2007 (7) |
| December, 2006 (5) |
| November, 2006 (9) |
| October, 2006 (11) |
| September, 2006 (14) |
| August, 2006 (11) |
| July, 2006 (15) |
| June, 2006 (8) |
| May, 2006 (10) |
| April, 2006 (12) |
| March, 2006 (3) |
| February, 2006 (7) |
|
|
|