|
9:4 PM Wednesday Aug 29, 2007
Comments: 0
I'm hoping that in a week or two I'll look back at the code I'm posting here and laugh at how silly it looks, but for now it's all I've got. I've made up this Contact Manager thingy, basically a CRUD app that is designed to get me thinking about how MonoRail works.
ScreenCast
Here's a screencast demonstrating my whiz bang demo.
Download
Here's my code for the demo. It uses ActiveRecord now and needs a database for a single table, contact. If you updated the connectionstring for ActiveRecord and call ActiveRecordStarter.CreateSchema, the correct table will be created for you.
8:43 PM Wednesday Aug 29, 2007
Comments: 0
This is a post about what NOT to do. We have a web service that returns us information about customers. Not much thought was ever put into how to use this service intelligently, the one way we were doing it worked, and we went about our business. So I've just described how to guarantee that a web service becomes a single point of failure in your application.
Let's say we have application A that has a critical dependency on application B. Application B has a dependency on a web service that's maintained somewhere else. Today for various reasons, the web service was ordered to be shut down. There's a replicated database we can use that contains pretty much the same data as the web service with a slight delay in the replication. So why not just switch over? Ugh...
First of all, there's a ton of code that goes something like this:
string url = ConfigurationManager.AppSettings["ws_url"];
WebServiceProxy proxy = new WebServiceProxy(url);
string xml = proxy.GetStuff();
SomeParser parser = new SomeParser();
ConcreteClass obj = parser.Parse(xml);
//ConcreteClass is defined from the web service
//all remaining code now utilizes the concrete class
What's the problem?
Well, that code is spread out in various manners in hundreds of spots throughout the application. So let's say I want to get the data from the replicated database, I now need to change every reference to point to this new data. But everything is bound to this stupid class defined from web service proxy, so that makes it suck more.
Now What
Well I tried to fix it tonight and after bout 2 hours of updating code, i was not even close so I through in the towel. What should I have done and what needs to happen?
Whatever data that is pulled back from the web service needs to be nicely tucked away behind an interface. All client code needs to be updated so that it has no clue where it's data comes from, all it knows is that interface, and that's it.
Construction of the system that will go retrieve the data from whereever, needs to happen in exactly one spot. This is a good time to introduce dependency injection into the mix, that's for sure.
This dependency has bugged me for awhile but because of all of the code changes I've avoided doing anything, but now our apps are dead in the water, so that may have moved the necessary changes up the priority stream, so to speak.
9:13 PM Monday Aug 27, 2007
Comments: 0
Trek to Hello World
OK. I started in on MonoRail today, very minimal code, but it's a start. The hardest part for me was setting up the bare minimum needed to render a Hello World type page. I'm a curious individual, and I not a fan of "magic" code and plumbing generated by IDE's. I wanted to learn what exactly was needed to render content in Monorail. This forced me to leave out Visual Studio altogether and use only Notepad++.
What Am I Building?
I want to build a website that uses MonoRail obviously, and uses Brail as the view engine. Why Brail? Why NOT? I don't know any of them and Boo is a weird enough name for a language, how can you not try it out at least? So Brail it is. All it needs to do is render a Hello World type page MonoRail-style.
Download Castle Bits
I used Build 518 of the Castle code located here. I've gotten in the habit of just grabbing the latest successful build from their build server to keep up on the latest, you can also just point to the trunk but I'm lazy and just want the last successful build.
My Thought Process
Since I knew nothing about MonoRail, very few details anyway, I figured I walk through the intro. There's the Getting Started section, which was a quick walk through of stuff. Next I started sifting through the User's Guide. Since I'd used the installer for RC2 at some point, I had the Castle MonoRail project wizard installed, so I created a new MonoRail project in Visual Studio. Of course that worked out of the box, so I tried re-creating the solution by selecting the bits from build 518. That didn't go so well. In hind sight there is a change I wasn't aware of, so I think I had recreated it properly but was not up on some of the changes(more on this later).
Since the manual creation of a MonoRail app skeleton in Visual Studio failed, I closed VS and opened Notepad++. Step 1 was to create a NAnt script to ease the building and rebuilding process from the command line. Step 2 was to define the structure of the folders, I followed the guidelines from here. The first go at this, I pretty much used the same code that's baked into the project template, remember, my goal is to just get my first view rendered by MonoRail and that's it.
So here are the exciting pieces so far, we have a controller HomeController.cs, a layout(this is like a master page in web form speak)default.boo, a view(this is like a content page)index.boo, an error view(just a view designated as the view that's gonna get displayed if the controller blows up for some reason)generalerror.boo. (the boo extension is important, I'll explain in just a bit).
OK Houston, Let's Light this Candle!
Armed with my nant script I try to build and one by one eliminate missing but required assembly references. It built, yay! Now we need to run it. I didn't feel like messing around with IIS, so i just opened a new command window, changed dir to where that nant script is and ran nant aspnet on the command line. This starts a new instance of the webserver that ships with .NET 2.0 on port 6969 pointing to the directory that the nant script was executed from. So at this point we should be able to type in http://localhost:6969/home/index.rails and voila!
What do we have? YSOD. That looks something like this. What gives? Remember I said that that boo extension was important? well apparently it changed from boo to brail, renamed the views to the brail extension and it works like a champ.(so now I have index.brail, default.brail, and generalerror.brail)
I know I said it was a Hello World but they have built in support for Prototype and Scriptaculous, so I couldn't resist throwing a little bit spice on the hello world example.
Get the code for this post here. It includes the needed Castle assemblies so it's about 1.2 mb.

5:31 PM Sunday Aug 26, 2007
Comments: 2
Where's My Favicon??
I was in the midst of a little facelift for this site and got to thinkin, my favicon is still the default subtext image. So I went and made a wicked cool image that I wanted to use as my favicon. Dropped in my slick ass favicon in the same location as the default subtext favicon, and voila. Nothin. Somehow, the default icon is still showing.
Obviously ...
Here's the thing. It wasn't obvious(to me) how to change this behavior. I actually checked out the source code for the subtext project and looked for any references that might override the favicon behavior and found nothing. I spent a long time looking and have given up. It was an animated favicon too, soooo cool(it probably isn't but it hurt the artsy side of brain creating it so I was excited about it).
Whining
I've whined about the source code capabilities(or lack there of) of dasblog and subtext. But it's pretty easy to get around, focus on the content, right? Who cares what it looks like.
Unfortunately for me, I care. So I go down the path of, "hey, write my own blog thingy again, blah blah blah"... who hasn't had that thought yet?? But after today's experience with my favicon, I got to thinking about architecture, good versus bad of course, and better versus worse etc.
I am/was in the middle of another post on user interfaces, and this one kind of stepped in front(or on top) of it. The essence of the post stems from a conversation I had with a colleage over the effectiveness of tools, web apps, really any application you can think of. Essentially it all boils down to the quality of the user experience. No matter how good your software is, it could be capable of coming up with a cure for diabetes or cancer, but as long as it causes the end user grief and is hard to use, it won't get used, period.
Good Enough Software
For me, good enough software is good enough until it gets in my way and causes me time I don't have. Subtext is that software, good enough until I need to change my favicon, then it becomes an issue that needs to be dealt with, consuming time I don't have available.
I started off blogging with my own home grown blog software, it wasn't great and did not have even a tiny fraction of the total features of dasblog or subtext or probably most other blog engines. I'm not knocking subtext, i'm just a picky user. So I figured this was a good time to rethink my home grown blog software.
I'm a huge fan of Castle's ActiveRecord. I know nearly nothing about MonoRail. BUT. I want good blog software that meets 100% of my needs, not most of my needs, but every last one of them. And I want to learn MonoRail beyond a hello world example. So that's the new side project. Build my own super simple blog engine geared towards one thing, answering all of my needs while demonstrating good architecture. I'll post the code as I go.
8:7 AM Saturday Aug 25, 2007
Comments: 0
Here's a handy one:
int[] intarray = new int[]{ 1, 2, 3 }; string[] stringarray = Array.ConvertAll<int, string>(intarray, delegate(int el){ return el.ToString(); });
Although I'd still rather have something like the following built into the framework:
int[] intarray = new int[]{ 1, 2, 3 }; string[] stringarray = intarray.To<string>();
2:50 PM Tuesday Aug 21, 2007
Comments: 4
The Problem
More often than not I encounter code like this where I work:
public class MyBusinessLogic{
public MyBusinessLogic(){}
public bool IsValid(Contract contract){
foreach (Participant p in contract.Participants){
Customer customer = Utils.GetCustomer(p.CustomerID);
if (String.IsNullOrEmpty(customer.Name))
return false;
}
return true;
}
}
And Utils is defined like this:
public class Utils{
public static Customer GetCustomer(int customerID){
string url = ConfigurationManager.AppSettings["mywebserviceurl"];
CustomerWebService service = new CustomerWebService(url);
return service.GetCustomer(customerID);
}
}
What's the Problem?
MyBusinessLogic is tightly coupled to the real webservice.
IsValid only works if your connection to the webservice exists.
How can you test IsValid without be sure of the results the webservice might return? You don't own the webservice, so there's no direct access to the data. There's no way to guarentee that your test that worked today will work tomorrow, if say the customer no longer fits your criteria anymore.
The only way to test this out is to waste a bunch of time data mining for use cases that might exercise your code.
Dependency Injection Please
What we have now is a dependency on the external resource, the web service. We need to extract this dependency, and we'll do that with an interface. This is the is what the interface looks like:
public interface ICustomerWebService{
Customer GetCustomer(int customerID);
}
Next, we're going to change MyBusinessLogic from this:
public class MyBusinessLogic{
public MyBusinessLogic(){}
public bool IsValid(Contract contract){
foreach (Participant p in contract.Participants){
Customer customer = Utils.GetCustomer(p.CustomerID);
if (String.IsNullOrEmpty(customer.Name))
return false;
}
return true;
}
}
...to having one constructor that requires an instance of something that implements ICustomerWebService. Then change the code in IsValid to use the instance passed to the constructor to get the customer like this:
public class MyBusinessLogic{
private ICustomerWebService _service;
public MyBusinessLogic(ICustomerWebService service){
_service = service;
}
public bool IsValid(Contract contract){
foreach (Participant p in contract.Participants){
Customer customer = _service.GetCustomer(p.CustomerID);
if (String.IsNullOrEmpty(customer.Name))
return false;
}
return true;
}
}
How Do You Test It??
One test could be written this way(using Rhino Mocks to mock the service):
[Test]
public void ContractShouldBeValidIfAllCustomerNamesAreNotEmptyAndNotNull(){
MockRepository mocks = new MockRepository();
ICustomerWebService service = mocks.Stub<ICustomerWebService>();
// Create the customer instance we're testing our code against...
Customer aCustomer = new Customer();
aCustomer.Name = "Chris Carter";
// Create the expectation that a call to an instance of something that implements
// ICustomerWebService.GetCustomer returns the customer we just
// created(our code doesn't know we made it up)
int customerID = 123;
Expect.Call(service.GetCustomer(customerID)).Return(aCustomer);
// Create the input that the business method expects to receive.
Contract contract = new Contract();
contract.Participants.Add(new Participant(customerID));
mocks.ReplayAll();
// THIS IS THE DEPENDENCY INJECTION !!!
MyBusinessLogic logic = new MyBusinessLogic(service);
// Verify that our business logic works against a service that returned a
// customer where the name was not null or empty.
Assert.That(logic.IsValid(contract), Is.True);
mocks.VerifyAll();
}
How Do You Send in the Real Service?
The real service just needs to have ICustomerWebService tacked on to the end of it's inheritance declaration. So it ends up looking something like this:
public class CustomerWebService : WebService, ICustomerWebService{
public CustomerWebService(string url){
this.Url = url;
}
public Customer GetCustomer(int customerID){
//here we would make the real call to the webservice,
//instead we'll just send back a new Customer.
return new Customer();
}
}
Since that method was the basis for defining the interface in the first place, it's already implementing the interface so we're done with that class. In our code whatever application is using the MyBusinessObject would be responsible for wiring up the real object. So the application code may be code in a server side click event that looks like this:
protected void btnVerifyCustomer_Click(object sender, EventArgs e){
string url = ConfigurationManager.AppSettings["webserviceurl"];
ICustomerWebService service = new CustomerWebService(url);
MyBusinessObject biz = new MyBusinessObject(service);
Contract contract = Contract.Find(345);
lblContractValid.Text = biz.IsValid(contract).ToString();
}
And better yet, you could move the code that constructs the service to a helper class like this:
public static class ObjectMother{
public static ICustomerWebService GetCustomerWebService(){
string url = ConfigurationManager.AppSettings["webserviceurl"];
return new CustomerWebService(url);
}
}
Download
Here's the code
8:44 PM Sunday Aug 19, 2007
Comments: 0
Here's the problem. You have some dot net code you need to call for whatever reason and you're sitting on an html page. How do you do it? Prototype to the rescue!
HttpHandlers are going to enable us to call dot net code from an html page. I'm gonna use the autocomplete textbox sample from the scriptaculous site for my example.
NOTE: I did this example without the use of visual studio.
Click here to view the code for the webhandler. This gets called as you type into the textbox.
Here is the example.
Download it here.
11:37 AM Sunday Aug 19, 2007
Comments: 0
I dug up the code I wrote for DNAP(Dot Net And Prototype) awhile back and decided that it is a useful idea afterall.
What is it?
It's the middleman I use to wire up my dot net code on the back end to my javascript on the front end. That's pretty much it in a nutshell.
Why?
I use AjaxPro on occassion, but I ran into a few problems with conflicts between prototype and the javascript that is rendered with AjaxPro. I'm sure there are ways around them but sometimes I don't feel like dealing with debugging javascript conflicts.
I liked a couple of things I was experimenting with too, like rendering validation code in the javascript method bodies so the server request isn't made if any of the necessary data is invalid.
I also wanted to be able to swap out JSON serializers if I felt like it. I used Jayrock which works well enough but who knows, maybe i'll need and/or want to use another one sometime. The plan is to have it wired up for swapping out libraries via web config.
Example Usage
Basically I need to be able to write some C# code, and without doing anything except inherit from a base page that does the magic. At it's simplist, you define a method that you need to access from the client side. Adorn it with the DNAP.GenericAjaxMethod attribute and make sure your page inherits from AjaxBasePage and you're good to go, the plumbing to call your method through the prototype.js Ajax object is wired up for you.
So click here to see the code behind that needs to be accessed from the clientside. It's got a SUPER complicated method.
Click here to view the sample page that uses the code.
This is still one of my favorite parts. Let's say you've set up your codebehind and now you want to use it from the clientside. What's the syntax? how do I use it? call the name of your page followed by ?json=howto and a test page is rendered that explains how to call whatever code you have set up in codebehind.
Download
Here is the code, DNAP.zip(133K).
6:20 PM Saturday Aug 18, 2007
Comments: 0
1.6 release candidate zero is out. And it's frickin awesome.
Before 1.6:
$$('input[type=checkbox]').each(function(el){
el.observe('click', function(e){
console.log(Event.element(e).id);
});
});
With 1.6, this now represents the element that fired the event:
$$('input[type=checkbox]').each(function(el){
el.observe('click', function(e){
console.log(this.id);
});
});
10:30 PM Thursday Aug 16, 2007
Comments: 0
I figured if I posted a really crappy design it would motivate me to make it better. So here it is!
9:30 PM Tuesday Aug 14, 2007
Comments: 0
That's what I just did on a copy of one of the stock Subtext themes. I love how you can build your own themes. I don't have much to compare it to, but in Subtext it's a matter of splashing some user controls around your design and that's pretty much the meat of it. This is cool. Now I'm off to Smashing Magazine for some inspiration.
6:8 PM Tuesday Aug 14, 2007
Comments: 0
I don't like how this blog is working out. Not the software, but my usage. I have yet to move my content that was created in Dasblog over. I have dead links. My previous scheme for referencing articles(guids) no longer works because I don't use that software anymore. This site is still using one of the default templates that ships with Subtext, which I'm not in love with.
Here is my promise(to me): By the end of this week.this blog will have a new look and feel representative of me. And by the end of this weekend I will move my old dasblog entries to this blog. Whew, I feel better now.
9:8 PM Sunday Aug 12, 2007
Comments: 0
I'm always trying to find another tool for my development belt. I've noticed is that's it's hard [for me] to break away from C#. Why is that? And by "break away" I mean, how many C# developers could, hypothetically speaking, survive in another environment? Today that is.
I don't do Java. I don't do Python. I don't do Ruby. However, I like things about all of them. My sticking point comes down to payback. If I learn Java today, does it help me tomorrow with the job(s) that pay my bills?
Is it really possible to become productive in a completely different environment? I have no idea, I create too many road blocks for myself on that front. I'm into the book the 4 Hour Work Week by Tim Ferris. I love the book. It's a quick read and it's packed with info to make you think. Really makes me think about how I choose to use my time. Why do you learn something new? For me, it's because it keeps me in the game so to speak. I get bored quickly. For me, that "something new" isn't necessarily a new app or framework, but actually more like technique, because that's what interests me. Some devs out there wanna learn the asp.net ajax framework, or WCF or whatever new framework MS has in the market, but not for the sake of knowing the guts, but more for the sake of having more acronyms on their resume.
Blogging is a good analogy for how I view learning frameworks versus really learning the technology underneath. If you haven't noticed by now, I suck at blogging, or at least I think I do, but that's why I'm blogging right now, I don't think I'm that good, I wanna get better, so I do more of it. Anywho, point is, I'd feel the same way regardless if I'm using Subtext, Dasblog, or some other blogging framework, if you're not good at the guts of the "stuff", it doesn't matter what framework you stick on top, you're still only as good as what the framework abstracted you away from.
If you've made it this far in the blog post then you've probably had as many margs as I have or you fell asleep and accidently hit the down arrow. I don't even know if I have a real point yet, but I think I do. As a developer, do you ask yourself, "why am I doing what I'm doing today?" Am I gonna make a difference today? if I were to make a difference today, what would that be?
And if you don't ask those questions, then what are you doing?
|
|