Thursday, December 30, 2010

DSL's or Domain Specific Languages

I saw this at Adobe MAX last year and I finally got a chance to try it out. DSL or Domain Specifically Languages are languages that only reside in a domain, in our case the web app we are developing. A good example of where DSLs are used is in gMail in the search box. If you haven't noticed it yet they are really handy, but they only work in gMail. Another good one is MS Outlook, they support crazy operators. Here is what one would look like:

dsl

I grabbed this one from gMail. The convention seems to use a keyword followed by a ":". In the case above it's "from:". I have only found DSLs helpfully for search boxes. If you are going to use DSLs you need to have legend so your users know what keys are available. You need to support it everywhere or tell the user where it is supported. If you are going to use them you should do whatever it takes in the keys and language to make it easier for the user. DSLs should be quick to use. Some advanced DSLs might look like this "from:(name:*joe)" or something like that or if operators are involved.

Once the user enters the DSLs you need to parse the string and do something with it. I create a query criteria snippet for sql or orm queries to allow the user to search across other things in the search box.

DSLs aren't really user friendly that's why I would recommend "search areas". Search areas are much easier to read, but less flexible. I posted on it early tonight here.

Getting all the URL params in Javascript

I've been updating a lot of href's lately and found these functions to be very handy. They basically take all the pars in a string and put them in a JS object. There is other functions out there that get you 1 url param, but I wanted them all. Reason being, is I need to see if that param exists and do some more checks on it for business logic, then take all the pars and turn then into a querystring and add them back to href again.


getURLParameters = function(url){

var results = {};

if(url == ""){
var url = window.document.URL.toString();
}
if (url.indexOf("?") > 0){
var splitURL = url.split("?");
params = splitURL[1].split("&");

for (var i=0;i var param = params[i].split("=");
if (param[1] != "")
results[param[0]] = unescape(param[1]);
else
results[param[0]] = "";
}

}

return results;
};
getURL = function(url){

if(url == ""){
var url = window.document.URL.toString();
}

if(url.indexOf("?") > 0){
var splitURL = url.split("?");
url = splitURL[0];
}

return url;
};


getURL() actually serves two purposes. If a url isn't passed in get the current url. And it also cleans off all of params so all you get the base url.

Search Areas

Had a task where a user didn't want to use the primary "search by" of searching in a list which is usually to search by "name" or "number". I did some research and found a way to keep the majority users satisfied, yet still giving the ability to the advanced user. Check it out...

Regular search box:
plain

Search box with search areas:
search_areas

It's actually not to tricky to do. It's just a table with two cells. The left cell is the input box with a style of no boarders and right cell is a div with a pic in it. When you click the div with the pic in it another div below is shown with absolute positioning. I will let you figure out the javascript and css. It's not too hard.

iStockPhotography currently has something similar.

MS SQL GUID lengths

Working with MS SQL and found out that errors with GUIDs are only thrown if the length of the GUID isn't met. The length of the GUID can be over the length of a regular GUID. This is kinda wierd because one would think this would error, but in fact MS SQL actually only will store the length of the GUID String up to the length of a normal GUID. Check out the examples below...


create table Snowboarder(
id uniqueidentifier not null,
name varchar(200) not null,
primary key (id)
)

/*--will throw an error---*/
insert into Snowboarder(
id,
name
)values(
'',
'Blank GUID'
)

/*--will work---*/
insert into Snowboarder(
id,
name
)values(
'D832DE4B-8453-4831-9505-6CA8CB4A4033',
'Regular GUID'
)

/*--will work, but it will only store the first guid---*/
insert into Snowboarder(
id,
name
)values(
'FE7A6068-080B-43B3-B392-3ABCE2E5FADF13725D91-B434-41B4-97A8-5607BF864EF5',
'Two GUIDS together'
)

/*--will throw an error because the first part is already in the db--*/
insert into Snowboarder(
id,
name
)values(
'D832DE4B-8453-4831-9505-6CA8CB4A40336448153D-A72F-42A8-BB35-17EE076D393C',
'Two GUIDS together with the first part already in the db'
)

ColdMVC: AJAX call

Trying to make ajax calls with ColdMVC was a little difficult for me because I struggle with routes, but I found a way. Here is what I did to make an AJAX call with ColdMVC.

First I created a function on a Controller so I can return "hi" back from the AJAX call. Notice I have @view json.cfm above the function. Since ColdMVC uses routes I need to send the data to a view and cfoutput it on the page.

TestController

/**
* @accessors true
* @extends coldmvc.controller
*/
component {

/**
@view json.cfm
*/
function testMethod(){
params.json = "hi";
}

}


Here is json.cfm. All that I do with it is output the variable "json".

json.cfm

<cfoutput>#params.json#</cfoutput>


And lastly I preform my AJAX call using jQuery. In the url setting of jQuery.ajax() I just link to the controller. In my case, my controller's name is TestController, so my route with just be "test" and then I can change out the method. In my case I am calling the "testMethod" on the controller.

ajaxCall = function(){

jQuery.ajax({
url:"#coldmvc.link.to('/test')#/test?returnformat=json",
type:"GET",
success:function(data){
jQuery("body").html(data);
}
});
};


If you want to return json back just go to json.cfm and wrap params.json in SerializeJSON(). Then in your ajax call, do jQuery.parseJSON(). This is the only way I can think of right now to make an ajax call with ColdMVC, if there are any other techniques I would be more then happy to listen.

Tuesday, October 19, 2010

CF9 hibernate file includes.

This was so cool I had to repost it. After you've defined a few hibernate.hbmxml files it gets annoying to have type the same properties on every class. My fingers were getting sore to complain a little. After a reference shown to me by Tony Nelson, Mark Mandal had a great post on "includes in the hibernate file". Check it out.

If you notice in the hibernate.hbmxml below there is a line that says "&common;".

hibernate.hbmxml

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
[ <!ENTITY common SYSTEM "common.hbm.xml"> ]
>
<hibernate-mapping>

<class entity-name="User" lazy="true" name="cfc:beforeandafter.app.model.User" table="`User`">
&common;
<property name="firstName" type="string">
<column name="First_Name" length="200" />
</property>
<property name="lastName" type="string">
<column name="Last_Name" length="200" />
</property>
<property name="emailAddress" type="string">
<column name="Email_Address" length="200" />
</property>
<property name="password" type="string">
<column name="Password" length="200" />
</property>
<bag name="projects">
<key column="User_ID" />
<one-to-many class="cfc:beforeandafter.app.model.Project" />
</bag>
<bag name="projectRatings">
<key column="User_ID" />
<one-to-many class="cfc:beforeandafter.app.model.ProjectRating" />
</bag>
</class>
</hibernate-mapping>


&common; points to file called common.hbm.xml. Within common.hbm.xml file I defined all my similiar properties and just include it on all my classes.

common.hbm.xml

<id name="id" type="int">
<column length="10" name="ID" />
<generator class="identity" />
</id>
<property name="isDeleted" type="boolean">
<column name="isDeleted" />
</property>
<property name="createdBy" type="int">
<column length="10" name="Created_By" />
</property>
<property name="createdOn" type="timestamp">
<column name="Created_On" />
</property>
<property name="updatedBy" type="int">
<column length="10" name="Modified_By" />
</property>
<property name="updatedOn" type="timestamp">
<column name="Modified_On" />
</property>

Make sure your include file ends in .xml and not .hbmxml otherwise it won't work. Made my life a ton a easier. Thanks Mark and Tony for sharing this.

Tuesday, August 10, 2010

ColdMVC: Plugging in fckeditor

I am working on a CMS app and I wanted the fckeditor in my app, while using ColdMVC. Here's what I did to get it in my app.

I downloaded the fckeditor and put it in the public folder, like below...

app
>public
>>plugins
>>>fckeditor

Next, on a custom helper called "util.cfc", I added a function called editor().
1. Create a fckeditor bean.
2. Set the properties of the fckeditor. Specifically the basepath is really important. I used $.config.get('assetPath') to get to the directory where the fckeditor is located.
3. Lastly, I wrap the fckeditor in ColdMVC's field() so it looks like the other fields.

<cffunction name="editor" access="public" output="false" returntype="string">
<cfargument name="name" required="true"/>
<cfargument name="value" required="false" default=""/>
<cfargument name="width" required="false" default="100%"/>
<cfargument name="height" required="false" default="300px"/>

<cfset local.bean = application.coldmvc.beanFactory.getBean("fckeditor")/>

<cfset local.bean.basePath = "#$.config.get('assetPath')#plugins/fckeditor/"/>
<cfset local.bean.instanceName = arguments.name/>
<cfset local.bean.value = arguments.value/>
<cfset local.bean.width = arguments.width/>
<cfset local.bean.height = arguments.height/>

<cfif not StructKeyExists(arguments,"label")>
<cfset arguments.label = $.string.humanize(arguments.name)/>
</cfif>

<cfoutput>
<cfsavecontent variable="local.field">
#local.bean.create()#
</cfsavecontent>
</cfoutput>

<cfreturn $.form.field(label=arguments.label,field=trim(local.field))/>

</cffunction>


Finally on my view, I call the helper function editor() and pass in my value. The use case below is for the editing a layout record for a cms app.

<cfoutput>
<c:form action="save" bind="layout">
<c:hidden name="id" value="#layout.id()#" />
<c:input name="name" value="#layout.name()#" />

#$.util.editor(label="Layout",name="layout.layout",value=layout.layout())#

<c:submit name="save" />
</c:form>
</cfoutput>


One thing to note is that if you want to bind the fckeditor textarea to an object, so it can be used in ColdMVC's populate() for a save, you will need to prefix your instanceName in the fckeditor. I do this by wrapping the fckeditor in a helper function and use the "name" argument like this...


<--- on my view I put in "layout.layout" as the name--->
#$.util.editor(label="Layout",name="layout.layout",value=layout.layout())#

<--- inside the editor()--->
<cfset local.bean.instanceName = arguments.name/>