Saturday, May 31, 2008

Python Strings

Oh yea... i forgot to mention a Python string feature that I liked.

You can have a string with quotes without having to escape the quotes. You start and end the string with triple quotes, and quotes in the middle are fine.

IE:

""""I like to be quoted!". swell, right?"""

That should make life easier as long as you don't frequently include """ as part of your string literals.

Did some work on DvdFriend

It's been a long time, but I finally did some work on DvdFriend.

There are now LISTS in the left column, one for Dvd and one for In Theaters. This is to bring attention to particular movies of interest. These are managed on a per-product basis in the product editor.

The product editor now has 3 checkboxes
- Show on COMING SOON - the right margin. Without this, movies would only show up if they're have a rating. But, since I want to show some Direct to DVD movies (ie: stargate continuum), they need to show up even if they're not rated yet.

- Also in the product editor, I added a text box so that image urls can be manually entered. This is for cases where the image doesn't exist on amazon yet, usually theatrical releases. I have to find other image sources that I can safely use. (The "Set Image" link opens a browser that allows you to search amazon images. That's been there all along.)



The new lists on the left aren't very manageable yet. It'd be nice to be able to order them how I want, but I'm not that ambitious today.

Now for the bad news. In order to get that list on the left, I had to modify the CSS. Previously, everything was centered. I used absolute positioning for the list, so if you resized the window there was a very good chance the list would overlap the main page. So, I attempted to fix it, but didn't do so 100%. The header and body aren't lining up exactly right.

On the bright side, though, it looks MUCH better in firefox than it did before.

I changes JAY'S BLOG from a link to a navigation button so that it would render better in fire fox.

Google App Engine

I just went through the GOOGLE APP ENGINE "Getting Started" guide. Neat stuff.

http://code.google.com/appengine/docs/gettingstarted/

The runtime is Python, which I haven't used before. I installed python, then just used notepad to write the code.

The fun thing about python is that spacing counts. You block code by tabbing them out the same distance.

Google currently allows everyone to have 3 applications online, while they're still in preview mode. Througout the guide, it mentions how you can use any python library for doing different types of things, but it comes with "Django" stuff already bundled. IE: Templates. (In asp.net world, a template is a master page)

The benefit of using the google app engine is that it has a bunch of stuff built in, such as a datastore. The data store is not a relational database, though they expose a GQL language, which is very sql like.

greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")

You can also do it programtically.

greetings = Greeting.all()
greetings.filter("author =", users.get_current_user())
greetings.order("-date")

You never actually have to create any data store objects to hold your data. That happens automatically when you save stuff. It looks to be an object database. Once you save an object of type GREETING, then you use GQL to query for types of GREETING. (I wonder what will happen as you modify GREETING)

I'm definitely not a python guy, but it was fun to play with the language a little bit. Google App Engine is trying to make development/hosting a lot faster. The SDK gives you everything you need to get started (local web server and data store), and deploying the application to production is a snap... you just execute a python script that takes care of it for you. The setup is minimal. Everything pretty much works right out of the box.

Tuesday, May 27, 2008

Membership Update

When you create a new profile, it sends you an email.
The details of the email are recorded in the EmailHistory table.
The activation codes and userid are stored in the Activation table.

I changed the approach slightly. The actual activation part isn't done yet, but the plan is:
When activate via the website, you must specify Email Address, Password, and Activation code.
If you just click the link in the email (which is the easier way to go), it will pass two activation codes.

http://localhost/clanfriend/membership/activate/code1/code2

That results in an ugly url, which is swell.

The email is sent and recorded, and the activation codes are recorded. Next, I have to implement the actual activation.

Getting closer, but there are still a lot of loose ends before I can put it up.

Mvc.Net mapping / Preview 3

I said in an earlier post that I would like to have a mapping feature so that you can indicate actions via configuration rather than code. My tenative plan was to create a new subclass of Controller called MapController. It would have a method like

MapAction(string actionName)

In my login example, I'd change my action to:

MapAction("Membership.LoginSuccess");
which would redirect to the Home

MapAction.("Membership.LoginFailed")
which would render the LoginFailed view.

MapAction would do a lookup from the configuration store then do a render or RedirectToAction.

Preview 3 actually makes this a lot easier, because the acton methods now return an ActionResult object.

The subclasses of ActionResult (that I can find) are:
- ContentResult
- EmptyResult ?? (do nothing?)
- JSonResult
- RedirectResult
- RedirectToRouteResult
- ViewResult

So far, I've used implicitly used RedirectToRouteResult and ViewResult. (Controller has a RedirectToAction method and a View method which genereates the appropriate result object automatically).

This means that all I'd really need to do is create the appropriate type of ActionResult object and populate it from configuration. The saving is that the objects already exist; in Preview2, I would've had to create my own objects. (I'm glad I didn't waste time on it, then).

Now, I'd do:
return MapAction("LoginMembership.Success")

It'd be neat, but I'm not in a huge rush to do it. We'll see if it magically appears.

Preview 3

Woo hoo!

http://weblogs.asp.net/scottgu/archive/2008/05/27/asp-net-mvc-preview-3-release.aspx

I just updated the test site to use it. Other than 1 case of me not following instructions, it went smooth.

The HTML helpers have been changed. I was using an Html.Textbox signature that's no longer valid. Apparently, they'd rather you build the htmlAttributes collection and pass that in. For now, I merely simplified the call losing the SIZE and MAXLENGTH properties.

There's a major change to the ACTIONS, which I like a lot. Previously, the methods were of type VOID, and you'd do things like:

RenderView("Index")
or
RedirectToAction("Logout")

Now, you change the VOID to ActionResult, and change the actions to:

return View("Index")
or
return RedirectToAction("Logout")

Now, rather than instructing it to do something, you're returning an object that tells it what to do. So, now its more testable. You can write a test for the action and make sure that it returns the ActionResult that it should. Sweet.

Also, the ViewData object has changed. It used to be ViewData<T> where T is the type of the object passed to the view. That has changed to ViewData.Model<T>. This allows ViewData to provide other types of things such as...ummmm... Hang on a sec. I was really excited about that until just now. I pulled up the intellisense and didn't get what I expected. I'll have to look into that more, though I like the approach in intent. It is a place where a lot more things can be exposed other than just your view data object. As the framework grows, they can keep adding new things to ViewData.

Back to the grind.

Activation Codes

I'm starting to test the site pretending to be a brand new user. I want to make sure someone can sign up, get verified via an email activation code, then function. There will be two types of new users: those who aren't associated to any families, and those who are.

You'll be able to activate your account either by clicking the link in the email, or by typing in Email Address, Password and Activation code on the activation page. I'm pretty sure that will bet two different actions.

Sunday, May 25, 2008

MVC.Net update

Greetings


Just an update to let you know that I am still very active with the MVC.NET and LINQ SQL stuff, its just that there hasn't been anything terribly new to report. I continue to use LINQ SQL for everything, even where it feels incorrect.


I added a bunch of permissions. They are represented as, basically, name value pairs. There isn't a table storing these permissions yet; there's just a view that infers the permissions.


Whoever creates a clan becomes the clan owner.

Whoever creates a family becomes the family owner.

Clan owners can edit their clans, the family in their clans, and the people in their family.

Family owners can edit their family, and the people in their family.

People can edit themselves.


Those rules are all brought together in a view. I have a permissions class with three properties: People, Clan, Family. Each of those subclasses has a property or method to represent the permission from the database. The properties/methods are really for convenience. If there isn't a property for a particular permission, there's a method that you call to pass the parameters.


The permissions are setup as



ObjectType - People, Clan, Family
ObjectId - PeopleId or ClanId or FamilyId
Objectname - Clan Name, Family Name, or People Id
User Name
Permission Name


IE:

Clan, 33, 'Jay and Gina', 'Jay', 'CanAddPeopleToFamily'



I have a static reference to an instance of the permissions class. It uses a ReaderWriterLockSlim to wrap up the reader methods and the load method. Each time a family or clan is created, the permissions cache is reloaded.


That's really a brute force approach, and its definitely temporary. The permissions should be lazy loaded per-object-per-user.


I built it so that families can belong to multiple clans. When you create a new clan, it gives you the option of bringing existing families to the new clan. That's going to need an approval process... maybe the family doesn't want to be copied. At the very least, they need the flexibility to decide for themselves.

The more I use MVC.NET, the more I think that it really needs a mapping layer. That may be an ill-informed opinion, but so far, that's what I've come up with. I have the MEMBERSHIP controller redirecting the the HOME controller after logging in. That should be configurable at some other level. I'm thinking about writing that level to test it out. We'll see.

Thursday, May 15, 2008

Logging in with MVC.net

The test site has been coming along pretty well, though not a lot of new stuff to speak of recently. I learned some new LINQ stuff, but nothing revolutionary.

At last, its time to add login capabilities. I installed the aspnet membership tables into the database, and setup the web.config. Then I created a new controller called LOGIN, and changed the LOGIN button (previously a dummy) to use it.




ShowLoginPage - I originally had a condition on the page that indicated which action to use depending on the current user's authenticated state. But, the point of this is to not do that kind of logic on the page, so I moved it to the ShowLoginPage action. If the user is authenticated, it redirects to the Logout action. Otherwise, it shows the login page.

Logout - Easy enough... Logout, then redirect to the home page. This shows that the controllers are aware of each other. I'm not sure if that's correct, but I don't know of another way to do it, so there you go.

Login - Users will login by email address. They'll have a username too, but that'll be for display purposes. The membership provider supports login by username, so I have to find the username by searching on the email address. The Find returns a collection of users. You can retrieve from the collection by username, but we don't have a username, so we have to enumerate to the first one. That was inconvenient. users[0] would've been swell.

I haven't handled a failed login yet. Essentially, I want it to load the same view but pass it an error message. That'll be new.

Sunday, May 11, 2008

So much for that idea

The whole "swappable dal" thing didn't work out. It could've, but it would've been too much work, so I dropped it. I can play with SubSonic and the entity framework some other times.

Selecting into a new object
I created a couple view in the database and added them to the dbml. Reading is not a problem, but I'm curious how it handles attempted updates. (It is a view, afterall).

I ran into a new LINQ situation. So far, all queries have been like:

from p in this.TableName
select p




But, I only wanted a few fields from the view. How do you do that? It can't return a known object because you're specifying the fields on fly. I determined that I have to create a new object that has properties for just the fields I want, and return a list<> of those. So, I created a FamilyMember object.

I don't know if that's the right way to do it, but it works. I read a LINQ book several months ago. I'll have to get back to it. I'll absorb a lot more on the second pass due to the rudimentary hands on.

Extend the View Object

I added a new method to the vwClanFamily class.





Its used on the page in the inner loop.

Wednesday, May 7, 2008

MVC / LINQ / SubSonic / Entity Framework

When I first started goofing around with MVC.NET a few days ago, I considered looking at the entity framework at the same time. Then I figured that LINQ and MVC.NET would be enough to keep me busy

Tonight, while looking around to find answers to some mvc questions, I came across this:
http://subsonicproject.com/

I watched one of the videos, and its pretty neat. SubSonic generates the DAL layer for you. It can also be setup to regenerate the DAL every time the web site starts. (You wouldn't want to do that in production, but convenient for development). I haven't played with at all; I just watched the video, but its interesting. It looks like PRE-LINQ LINQ. I wonder if LINQ is going to be problem for them.

One nice feature of SubSonic is scripting. It creates an external tool that will script the entire db, including data, so that you can check it in. Cool.

I thought about using SubSonic for some portion of the demo site, but that'd be a pain. Rather than mentally battle over LINQ, Entities, and SubSonic, I'm going to refactor the stuff so it can use any of the three. If it works (it should), it'll allow me to play with all three. (Honestly, though, I don't plan on digging into the entity framework just yet).

I noticed one thing in SubSonic that I wasn't able to do in LINQ (at least not on the first attempt. I only spent 3 seconds on it): Partial classes. In LINQ, as shown in a previous post, I can create a partial class for the dataContext object.

I did this:

List people = _db.GetAllPeople(); //or something like that

I wanted to do:

List people _db.People.GetAllPeople();

As shown, it would have to be a static method, which isn't what I want. So, obviously, its not going to work as intended. But, the subsonic api works differently, and you can create a partial class at the table level.

So, my next task is to create an interface, a factory, and multiple implementations of the interface so that I can play with multiple DAL technologies. Stay tuned.

Monday, May 5, 2008

Tonight's MVC progress

Yesterday's post didn't go so well. The code and HTML isn't rendering correctly. Lets try screenshots.

I have the edit functionality working. I ran into one glitch along the way. The edit page is a form. I set the action attribute using Url.ActionLink, but it didn't do anything. It kept posting back to the view page. "View Source" revealed that there are 2 form tags: 1 for the master page, plus the one I added. I removed the one from the master page, and it started working. (If I wasn't able to remove the parent form tag, what would I have to do? Maybe change the parent's action instead?)



Here's the main page. Its using the master page and css provided by the template. Its nicer than anything I could've done, so I'll keep it.





The MVC links are:
Clan Admin - http://localhost:1653/ClanAdmin/Index
Home - http://localhost:1653/Home/Index
About - http://localhost:1653/Home/About
Login - http://localhost:1653/Home/Login

When you click Clan Admin, you get the really ugly list of people.




The Edit page url is http://localhost:1653/ClanAdmin/EditPerson/3. The last segment is the people id.


When you click save, it submits to /ClanAdmin/UpdatePerson/3.


Here's the UpdatePerson action.

BindingHelperExtensions.UpdateFrom is neat. It looks at the posted form and maps the element names to the object property names. So, name your controls the same as the properties, and you can do the update in one swoop. It also provides measures to prevent certain properties form being updated, but I'm not using that here.

Yesterday I mentioned that I would need to create a partial class to add more specific functionality to ClanContextData. I was going to add a class to APP_CODE, but that doesn't exist in ASP.NET. Its been removed from the ADD ASP.NET FOLDER menu. So, I put it in the controller folder, which I now think is the correct place. (The REAL right place would be a separate assembly that contains all of the business logic. But, I don't have that piece. It's all in Controllers).


Load / Modify / Reload / Save

I just read through part 4 of one of tutorials I've been using to go through the MVC stuff.

http://weblogs.asp.net/scottgu/archive/2007/12/09/asp-net-mvc-framework-part-4-handling-form-edit-and-post-scenarios.aspx

Unfortunately, it suggests that to do an update, you need to reload the object, update it, then save it. So, if the values have changed since you populated the page, then you'll just overwrite them.

Sunday, May 4, 2008

MVC.Net and Linq to SQL

I was up until 4:30am Friday night working on a database thing for work. I spent most of the day Saturday, on and off, doing the same thing. The good news is I learned all that I needed to learn, solved the problem, and built a pretty thorough test harness and MbUnit tests for it. Swell. It violated my "don't work on weekends" rule, but it was for a good cause. I had to learn new stuff, so it was to my benefit.

Anyhoo, that's a different story for another day.

When Sunday evening rolled around, we sat down to watch an Indiana Jones movie or 2 (or 3). I'm mentally unable to simply sit there and watch a movie that I've already seen, unless I'm tired or uncharacteristically mentally lazy. Typically, I need something to work on at the same time. (Well, it depends on the movie too, I suppose).

Furthermore, I've been slacking for a long time. Over the last few months, I've played A LOT of Burnout Paradise. For a while, I was feeling guilty about gaming instead of something productive, but I quickly came to terms with it. If I want to take a break, I can take a break. This one just happened to be longer than usual. Oh well.

So, now that i've thoroughly beat Burnout Paradise, and I've done enough "work" work, its time for something new. I decided to jump on this MVC.NET thing that I've been hearing so much about. And, at the same time, I might as well start using LINQ for SQL. (I dabbled with LING for collections a bit, but nothing serious).

The MVC.NET web template gets you going pretty good. It creates the MODELS, VIEWS, and CONTROLLERS folders.

Create Database
I have a vague about the website I'm going to build from this stuff, but its not real important. I started with an empty database and created a single table: PEOPLE. It has the following fields;
- PeopleId (PK int identity)
- FirstName
- LastName
- EmailAddress (nullable)
- BirthDate (nullable)
Simple... no big deal.

Create DBML
I ended up using VS2008 to crate the DBML file. Before that, I used SQLMetal just for kicks. It worked swell.

I then ran a couple quick tests on the DBML file just to get a feel for ole betsy.

ClanDataContext db = new ClanDataContext();
People people = new People();
people.FirstName = "first";
people.LastName = "last";
people.EmailAddress = "jay@allardworks.com";
people.Birthday = new DateTime(1972, 7, 2);
//PeopleId deafults to 0
db.Peoples.InsertOnSubmit(people);
db.SubmitChanges();
//PeopleId is now the new identity value

Sweet. LINQ is cool.

Modify the MasterPage
The MVC site template comes with a master page. I added an admin link to the left margin.


  • <%= Html.ActionLink("Clan Admin", "Index", "ClanAdmin") %>

  • Parameter 1 is the text that appears in the link.
    Parameter 2 is the name of the action.
    Parameter 3 is the controller.

    The existing links on the page did not specify the controller, because there was only one to start with. Once I added my ClanController, I had to change the existing links to specify the third parameter.

    The link renders as: http://localhost:1653/ClanAdmin/Index (Controller/Action)

    Controller Class
    Next, create a controller class. The controller specified is ClanAdmin. The name of the specified action was Index, so create a method called Index.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using MvcTest1.Models;
    namespace MvcTest1.Controllers
    {
    public class ClanAdminController : Controller
    {
    public void Index()
    {
    ClanDataContext db = new ClanDataContext();List people = (from p in db.Peoples select p).ToList();
    RenderView("Index", people);}

    public void EditPerson(int peopleId)
    {
    RenderView("EditPerson", peopleId);
    }
    }
    }

    That's the full Controller class. Index is the action we've already mentioned. It uses the linq entity class to get a list of all of the people. It passes it to the view via the second parameter of the RenderView method.

    The first parameter is the name of the view. So far, it seems that view names may commonly match action names, at least for navigation purposes. We have an action called index that loads a view called index.

    The second action, EditPerson, comes into play later.

    Index View
    Thew view page goes into VIEWS/CLANADMIN. VIEWS is a fixed name. CLANADMIN is the name of the controller. (What if you want multiple controllers to share the same view? Is that practical and/or possible?)

    By default, a view is just an ASPX page. However, you can change that behavior somewhere. (I'm not there yet).

    Here comes an inconvenience: When you add the MVC view page, it doesn't give you the option to select a master page. You have to create the page, assign the master page, and lop off the junk you no longer need.

    If you look back to the controller code, you'll see that the second parameter of RenderView is a List. That parameter is called ViewData, and it may be anything type you desire. In order to make the view page aware of it, you must make it a generic of that type.

    Controller / Action Method

    ClanDataContext db = new ClanDataContext();List people = (from p in db.Peoples select p).ToList();
    RenderView("Index", people);}

    NOTE: Will create a partial class to add a new method to ClanDataContext: GetPeople();

    View Page

    public partial class Index : ViewPage>
    {
    }

    Now, the ViewData property is a List.

    Lets recap. The controller retrieves the list of people and passes it to the view. Now, to keep the name VIEW honest, we must display the data.

    It looks like that while the use of ServerControls is allowed, its not encouraged. Instinct is to drop a DataGrid on the page and bind it to the list. But, all examples so far just use inline code. We end up with smaller more precise html, but we lose the flexibility of the grid control. I'm eager to see how that pans out. (I tried creating a Table object manually in code behind, but the namespace isn't included by default. I took that as a hint to not use it. For now, I'm keeping it simple. Its my first night.)

    I went with the flow and added inline code to the ASPX page.


    <% foreach (var p in ViewData) { %>
    <% } %>
    <%= p.PeopleId %><%= p.LastName %>, <%= p.FirstName %><%= p.EmailAddress %><%= Html.ActionLink("test", "EditPerson", new { controller = "ClanAdmin", peopleId = p.PeopleId }) %>



    I really don't like that. It brings me back to ASP. The difference, though, is that it'll all be compiled so you don't have context switching like ASP, but its still ugly. Not very OOPish, is it?

    I had a problem with Html.ActionLink. The second parameter is the action. Nothing I've found via google shows that parameter being specified. The anonymous third parameter becomes a RouteValueCollection. All samples I've seen specify the action there. But, when I do it, I get a method overload exception. Its trying to use the (string, string) signature, which is invalid. (This may be due to the fact that I'm using Preview 2. Most samples are Preview 1).



    Edit View
    Now that we have a list of people from the database, I'd like to edit a person. Notice the Html.ActionLink in the last cell. It creates a link with text of "test". It'd be better to use p.LastName or something, but its junk code. The second parameter is the name of the action. Earlier, we saw the action defined in the controller class as shown:


    public void EditPerson(int peopleId)
    {
    RenderView("EditPerson", peopleId);
    }

    That's not how it will end up looking. The second parameter should be a PEOPLE object, but for now, I'm just passing the PEOPLEID to show that it works.

    To support this, I created a second view called Edit, then simply print the ID (stored in ViewData)

    Class: public partial class EditPerson : ViewPage
    ASPX Code:

    ID: <%= ViewData %>



    Super. But, as I expected, it didn't work. The existing routes all treat ID as string, so there was a data type problem. I added a new route.


    routes.Add(new Route("{controller}/{action}/{peopleId}", new MvcRouteHandler())
    {
    Defaults = new RouteValueDictionary(new { peopleId = -1 })
    });

    I thought that would work, but it didn't. Then I remembered that the first matching route found wins. So, I moved that route to the top of the list, and now it wins. (I'm not 100% convinced this is all correct yet. This new route may be getting picked up in other scenarios when it shouldn't be. We'll see.)

    Now, when you click the TEST link, it goes to the EDIT page and displays the PeopleId.

    The URL to the edit page is:
    http://localhost:1653/ClanAdmin/EditPerson/3
    The page then shows the #3.
    Because nothing is implemented yet, you can change 3 to any integer that you want, and it will display.

    That's it for Tonight
    The next steps are:
    - Use Linq to retrieve the people object. (Create the partial class mentioned earlier, and add a GetPerson(int peopleId)
    - Create the edit page. MVC.NET has some stuff to make this easier. I need to learn the details.
    - Figure out how to save. When saving, do I have to load the People object, change values, then save? Does MVC have a standard approach to this? (The People object will most likely be persisted somewhere. I don't know if that's my task, or something MVC helps with. If we do the reload/modify/save, then we lose Linq's conflict resolution capabilities. We want to keep that.)