Learning Enough to be Dangerous – Go From Being a Novice to an Advanced Developer

current courses at Learn Enough to be Dangerous

While I think the venerable Ruby on Rails Tutorial is still the best way to learn Rails, jumping into this tutorial can be challenging for those who are new to programming.

With the fourth and latest beta of Rails 5 being released this past week, it is appropriate that Michael Hartl, the creator of the Rails Tutorial, has devised yet another tool to help new and experienced developers learn enough to make the most of it.

Appropriately, the title of this site is “Learn Enough to Be Dangerous”.

current courses at Learn Enough to be Dangerous

As of the end of April 2016, only three “Learn Enough” courses are available: Command Line, Text Editor, and Git. However, future courses including HTML, CSS & Layout, JavaScript, Ruby, Sinatra, and the Ruby on Rails Tutorial for Rails 5 are scheduled to be added.

The cost is $29 per month, the same as Code School (if you subscribe on a per-month basis to CS). Unlike CS, there is not yet a discounted yearly rate.

Since I’m excited to see the new Rails Tutorial as soon as it’s released, I’ve decided to sign up. Were it not for the access to this mammoth resource, I don’t know that I would pay this price – but considering that the Rails Tutorial with screencasts generally costs about $150 by itself, it seems like a reasonable deal. At any rate, I’ve come to expect only the highest quality learning materials from Dr. Hartl, so I am definitely looking forward to the upcoming classes.

Fun with CFAJAXPROXY on ColdFusion 11

my Stack Overflow question

In the course of moving an application from ColdFusion 8 to ColdFusion 11, I came across some strange behavior caused by the CFAJAXPROXY tag.

In CF8, a particular CFCOMPONENT called ProjectBeanService had proxies set up for its methods in the rendered JavaScript like this:

var _cf_ProjectBeanService = ColdFusion.AjaxProxy.init('/components/ProjectBeanService.cfc', 'ProjectBeanService');
_cf_ProjectBeanService.prototype.get = function(sPropertyName, sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "get",  {sPropertyName:sPropertyName, sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};
_cf_ProjectBeanService.prototype.getAll = function(sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "getAll", {sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};
_cf_ProjectBeanService.prototype.set = function(sPropertyName, oPropertyValue, sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "set", {sPropertyName:sPropertyName, oPropertyValue:oPropertyValue, sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};

However, in CF11, more proxies were created:

/* <![CDATA[ */
var _cf_ProjectBeanService = ColdFusion.AjaxProxy.init('/components/ProjectBeanService.cfc', 'ProjectBeanService');
_cf_ProjectBeanService.prototype.get = function(sPropertyName, sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "get",  {sPropertyName:sPropertyName, sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};
_cf_ProjectBeanService.prototype.getAll = function(sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "getAll", {sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};
_cf_ProjectBeanService.prototype.set = function(sPropertyName, oPropertyValue, sBeanType, nID, sSection, nRevision) { return ColdFusion.AjaxProxy.invoke(this, "set", {sPropertyName:sPropertyName, oPropertyValue:oPropertyValue, sBeanType:sBeanType, nID:nID, sSection:sSection, nRevision:nRevision});};
_cf_ProjectBeanService.prototype.get = function(sBeanName, sPropertyName) { return ColdFusion.AjaxProxy.invoke(this, "get","4789898A8974AC60", {sBeanName:sBeanName, sPropertyName:sPropertyName});};
_cf_ProjectBeanService.prototype.destroySessionBean = function(sBeanName) { return ColdFusion.AjaxProxy.invoke(this, "destroySessionBean", "4789898A8974AC60", {sBeanName:sBeanName});};
_cf_ProjectBeanService.prototype.createSessionBean = function(sBeanName, sBeanType, sDAOName) { return ColdFusion.AjaxProxy.invoke(this, "createSessionBean", "4789898A8974AC60", {sBeanName:sBeanName, sBeanType:sBeanType, sDAOName:sDAOName});};
_cf_ProjectBeanService.prototype.getAll = function(sBeanName) { return ColdFusion.AjaxProxy.invoke(this, "getAll", "4789898A8974AC60", {sBeanName:sBeanName});};
_cf_ProjectBeanService.prototype.getSessionBean = function(sBeanName) { return ColdFusion.AjaxProxy.invoke(this, "getSessionBean","4789898A8974AC60", {sBeanName:sBeanName});};
_cf_ProjectBeanService.prototype.set = function(sBeanName, sPropertyName, oPropertyValue) { return ColdFusion.AjaxProxy.invoke(this, "set", "4789898A8974AC60", {sBeanName:sBeanName, sPropertyName:sPropertyName, oPropertyValue:oPropertyValue});};
_cf_ProjectBeanService.prototype.reInitSessionBean = function(sBeanName, argument1, argument2, argument3, argument4) { return ColdFusion.AjaxProxy.invoke(this, "reInitSessionBean", "4789898A8974AC60", {sBeanName:sBeanName, argument1:argument1, argument2:argument2, argument3:argument3, argument4:argument4});};
/* ]]> */

This made no sense to me, as the ProjectBeanService class only had the three methods declared that were proxied in CF8. I looked at the ProjectBeanService.cfc file:

<cfcomponent displayname = "ProjectBeanService" extends = "com.AjaxBeanService">

    <cffunction name = "getBean" access = "private" returntype = "any">
        <cfargument name = "sBeanType" type = "string" required = "yes">
        <cfargument name = "nID" type = "numeric" required = "yes" hint = "ProjectID or ImpactID">
        <cfargument name = "sSection" type = "string" required = "no" hint = "ProjectSection or ImpactSection" default = "">
        <cfargument name = "nRevision" type = "numeric" required = "no" hint = "Commitment Revision" default = "0">

        <cfset var oBean = createObject("component","com." & sBeanType).init(nID,sSection,nRevision)  />

        <cfreturn oBean />

    </cffunction>

    <cffunction name = "set" access = "remote" returntype = "void">
        <cfargument name = "sPropertyName" type = "string" required = "yes">
        <cfargument name = "oPropertyValue" type = "string" required = "yes">
        <cfargument name = "sBeanType" type = "string" required = "yes">
        <cfargument name = "nID" type = "numeric" required = "yes">
        <cfargument name = "sSection" type = "string" required = "no" default = "">
        <cfargument name = "nRevision" type = "numeric" required = "no" default = "0">
        <cfset var oBean = StructNew() />
        <cftry>

            <cfset oBean = getBean(sBeanType, nID, sSection,nRevision) />
            <cfset oBean.set(sPropertyName,oPropertyValue) />
            <cfcatch type = "any">
                 <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

    <cffunction name = "get" access = "remote" returntype = "any">
        <cfargument name = "sPropertyName" type = "string" required = "yes">
        <cfargument name = "sBeanType" type = "string" required = "yes">
        <cfargument name = "nID" type = "numeric" required = "yes">
        <cfargument name = "sSection" type = "string" required = "no" default = "">
        <cfargument name = "nRevision" type = "numeric" required = "no" default = "0">

        <cfset var value = "" />
        <cfset var oBean = StructNew() />

        <cftry>
            <cfset oBean = getBean(sBeanType,nID,sSection,nRevision) />
            <cfset value = oBean.get(sPropertyName) />
            <cfreturn value />

            <cfcatch type = "any">
                    <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

     <cffunction name = "getAll" access = "remote" returntype = "struct">
        <cfargument name = "sBeanType" type = "string" required = "no" default = "ProjectBean">
        <cfargument name = "nID" type = "numeric" required = "yes">
        <cfargument name = "sSection" type = "string" required = "no" default = "">
        <cfargument name = "nRevision" type = "numeric" required = "no" default = "0">

        <cfset var oBean = StructNew() />
        <cfset var oStruct = structNew() />

        <cftry>
            <cfset oBean = getBean(sBeanType,nID,sSection,nRevision) />
            <cfset oStruct = oBean.getAll() />
            <cfreturn oStruct />

            <cfcatch type = "any">
                  <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

</cfcomponent>

I saw that this class extended another class, AjaxBeanService:

<cfcomponent displayname = "AjaxBeanService" extends = "com.AbstractAjax">

    <cffunction name = "createSessionBean" access = "remote" returntype = "struct">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfargument name = "sBeanType" type = "string" required = "yes">
        <cfargument name = "sDAOName" type = "string" required = "yes">
        <cfset var oBean = StructNew() />
        <cfset var oBeanArguments = ARGUMENTS />
        <cfset var oDAO = application[sDAOName] />

        <cftry>
            <cfset oBean = createObject("component","com." & sBeanType) />

             <!--- delete first 3 elements from arguments array --->
            <cfset ArrayDeleteAt(oBeanArguments,1) />
            <cfset ArrayDeleteAt(oBeanArguments,1) />
            <cfset ArrayDeleteAt(oBeanArguments,1) />

            <!--- make the DAO object the first argument --->
            <cfset ArrayPrepend(oBeanArguments,oDAO) />

            <cfset oBean.init.apply(oBean,oBeanArguments) />
            <cfset SESSION.beans[sBeanName] = oBean />
            <cfreturn oBean.getAll() />
            <cfcatch type = "any">
                <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
    </cffunction>

    <cffunction name = "destroySessionBean" access = "remote" returntype = "struct">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfset rc = StructDelete(SESSION.beans, "#sBeanName#", "True")>
    </cffunction>

    <cffunction name = "reInitSessionBean" access = "remote" returntype = "struct">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfargument name = "argument1" type = "any" required = "no" default = "">
        <cfargument name = "argument2" type = "any" required = "no" default = "">
        <cfargument name = "argument3" type = "any" required = "no" default = "">
        <cfargument name = "argument4" type = "any" required = "no" default = "">
       <cfset var oBean = StructNew() />
        <cftry>
            <cfset oBean = getSessionBean(sBeanName) />
            <cfset oBean.init(oBean.getDAO(),argument1,argument2,argument3,argument4) />
            <cfset SESSION.beans[sBeanName] = oBean />
            <cfreturn oBean.getAll() />
            <cfcatch type = "any">
                <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
    </cffunction>

    <cffunction name = "getSessionBean" access = "remote" returntype = "any">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfset var oBean = StructNew() />
        <cfif StructKeyExists(SESSION.beans,sBeanName) >
            <cflock scope = "session" type = "readonly" timeout = "5" throwontimeout = "yes">
                <cfset oBean = Duplicate(SESSION.beans[sBeanName]) />
            </cflock>
        </cfif>
        <cfif StructIsEmpty(oBean)>
            <cfthrow errorcode = "500" message = "No bean found by the name '#sBeanname#'" />
        <cfelse>
            <cfreturn oBean />
        </cfif>
    </cffunction>

    <cffunction name = "set" access = "remote" returntype = "void">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfargument name = "sPropertyName" type = "string" required = "yes">
        <cfargument name = "oPropertyValue" type = "string" required = "yes">
        <cfset var oBean = StructNew() />
        <cftry>
            <cfset oBean = getSessionBean(sBeanName) />
            <cfset oBean.set(sPropertyName,oPropertyValue) />
            <cfcatch type = "any">
                 <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

    <cffunction name = "get" access = "remote" returntype = "any">
        <cfargument name = "sBeanName" type = "string" required = "yes">
        <cfargument name = "sPropertyName" type = "string" required = "yes">

        <cfset var value = "" />
        <cfset var oBean = StructNew() />

        <cftry>
            <cfset oBean = getSessionBean(sBeanName) />
            <cfset value = oBean.get(sPropertyName) />
            <cfreturn value />

            <cfcatch type = "any">
                    <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

     <cffunction name = "getAll" access = "remote" returntype = "struct">
        <cfargument name = "sBeanName" type = "string" required = "yes">

        <cfset var oBean = StructNew() />
        <cfset var oStruct = structNew() />

        <cftry>
            <cfset oBean = getSessionBean(sBeanName) />
            <cfset oStruct = oBean.getAll() />
            <cfreturn oStruct />

            <cfcatch type = "any">
                  <cfset sendError(cfcatch.ErrorCode,cfcatch.message) />
            </cfcatch>
        </cftry>
     </cffunction>

</cfcomponent>

This class contained the methods that were showing up in the rendered code on CF11.

I found that at least part of this issue was most certainly a bug in CF8 that had been corrected in CF9. In CF8, a child class apparently did not have access to the methods of the parent class via CFAJAXPROXY if the parent class methods were marked as having “remote” access. No longer, in CF9 and subsequent versions. Source (in comments): Ask a Jedi: ColdFusion Ajax example of retrieving fields of data (2)

However, that did not explain why the parent class method proxies were being rendered AFTER the child class methods, thus preventing the child from overriding the parent class methods.

So far, the only fix I have is to mark the parent methods access attributes as “public” rather than “remote”. This seems like a poor way to do it, and may yet have unintended consequences, but without writing new JavaScript to avoid the use of CFAJAXPROXY, this may be the best solution for now.

Incidentally, an article on doing that very thing is here, in case anyone needs it: Creating A Remote AJAX Proxy In Javascript Without ColdFusion 8’s CFAjaxProxy

If anyone has any better suggestions on how to fix this problem, I’ve submitted it as a question on Stack Overflow.

my Stack Overflow question

Using Macrium Reflect to Clone a Windows PC

Windows 7 imminent HDD failure

Like so many developers are, I am the IT guy for my family as well as being one “in real life”. Today, I was confronted with an issue that was one of those hardware-related issues that crop up from time to time, and those can be just as difficult (if not more so) than software problems. I got a call telling me that Windows was popping up an alert window that warned of impending doom with the hard disk drive.

Windows 7 imminent HDD failure
A message similar to this one appeared repeatedly.

I used to deal with hardware issues all the time, and Norton Ghost was my software of choice for such problems in years past. However, I didn’t have a copy on hand, and searched for an alternative. Fortunately, I found out about Macrium Reflect. After an emergency trip to Best Buy for a new drive, I was able to clone the old hard drive in about twenty-five minutes using the free version of Macrium Reflect!

Macrium Reflect screen

The steps for using Macrium were very simple. Just make certain that you know which drive is which when you’re using the software; otherwise, you’ll end up erasing your old hard drive.