Announcing TimesheetToaster.com

It’s currently in a pre-release state.  After a little more work and testing, we’ll be inviting a limited number of people to use and try it out as an Alpha product.  After that, there will be a public Beta.  Or so the plan goes. TimesheetToaster.com

 
January 22, 2010 23:07 by josh
E-mail | Permalink | Comments (1) | Comment RSSRSS comment feed

Yep I’m going to do it

http://github.com/jcoffman/perforated-blog

more to come as time permits

 
January 13, 2010 15:32 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

The Pirate Way

Yes, that is George Washington crossing the Delaware. No, I am not suggesting that Washington was an actual, unlawful pirate. I condemn the immoral, brutal acts of real pirates; it’s the principles embodied that I admire. To me, a pirate represents freedom and the resolve to fight for it. In that regard, George Washington and the Founding Fathers represent my ideal of a pirate.

Current events in my country and professional life have prompted this post. There is much to admire and respect about my country, but which is being dismantled by the current administration. I see the same malaise set in within my professional world. The hard truth is that security is an illusion with a heavy price that buys you nothing but misery and failure in life. Liberty, morality, responsibility are the only way to succeed.

In the corporate world, as in daily life, making tough decisions and taking bold actions is something most people avoid. That is especially true when there are significant or dire consequences. When guided by common sense and good morality/ethics, those who take those risks are usually rewarded. When the safer road is followed, there is little or no success. What little success there is is mostly a second level benefit of those that took the risks. This is the path of fear, bureaucracy, and big government. And it’s usually laden with advertising for compassion, fairness, and community responsibility. There is nothing compassionate about losing freedom, encouraging failure, and lowering expectations.

Pirates take risks. Pirates rise up and rebel against oppression, and are rewarded for their resolve. Liberty and responsibility are the only way to succeed. AND it’s a reward we all share – a rising tide lifts all boats. There is no limit to growth and success that prevents others from sharing when some succeed. We all win if we choose to play.

This is so evident in business; it is often a complaint among those that work in large corporations, and yet so many chose to ignore it. So many projects fail because no one wants to make decisions and be responsible. Yet so many want to take credit in any success. That’s one big, hairy, headless ego monster that’s eats up everything around it, my friends.

I am not discouraged, because every wrong attempt discarded is another step forward. – Thomas A. Edison

Rebel for individuality, be resourceful, take a chance, make a decision, push for better, be open to improvement, and always take responsibility. That’s the pirate way. That’s why pirate ships worked against against a much better prepared, armed, and trained British Navy all those years ago. The captain took action, and was held responsible. The crew followed and took initiative. Those that didn’t were put off the ship. The bad captain was overthrown. The lesson is that each wrong attempt is an opportunity for improvement and success when you have freedom and responsibility.

As a man thinketh in his heart, so is he. – Proverbs 23:7

That which shackles you in your current life is almost assuredly in your own mind. It is the invisible cage we put ourselves in. Do you want to succeed in your personal or professional life? Do you want to make a difference in life? Be a pirate! Take action, learn, improve, and you will succeed. Everyone will benefit from it; your family, your community, and your country.

 
January 12, 2010 07:00 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Why Pirates are better than Ninjas

Disclaimer

I have some personal admiration for the dedication and aesthetics of ninjas and samurai. I just find the pirate embodies more of the values I embrace. That’s not to say their history and brutal practices, but rather the liberty, individuality, and creativity are things to admire and even strive to. In the real world, this also means adhering to common sense and morality. Actual real pirates are generally horrible people lacking a moral compass. From here on out, I mean the idealism embodied by a pirate and not the scoundrel.

Liberty

A pirate is a free individual; unbridled by any man or government. Ninja’s are servants who exist to obey their master. Liberty is the first and most important value of a pirate.

Creativity

A pirate is resourceful and creative in solving problems. Always looks for new and better ways to attack a problem. A ninja is dependent on practice and aesthetics; his approach to any problem always comes from tradition.

Responsibility

All pirates take and are held responsible for their actions. If not on their own then by their crewmates; or possibly by their captures. Responsibility can be painful, but leads to greater success. A ninja is traditionally bound to hold themselves responsible or be shamed. The modern, westernized ninja does not adhere to this. Instead he shuns responsibility and simply moves on.

Decisive

A pirate makes decisions and takes actions. Even serving on a ship under a captain is the explicit choice of the pirate. No pirate would ever willingly live in compulsory servitude, and would fervently rebel against any loss of liberty by compulsion. The ninja lives to obey and lacks or has limited free will of his own. A ninja without orders has no direction. A pirate would take the lead if no one else does, or he is no longer satisfied with the current leadership.

Conclusion

Life, Liberty, and the pursuit of happiness are the way of the pirate; to which I add a moral & ethical compass. These are the American ideals, and the values I embrace. These ideals lead to personal and professional success, and is beneficial for both the individual and the community. It would greatly benefit many people and humanity as a whole if more people embraced the pirate way. The ninja way is content with dictators, fascists, and socialism; things that oppress people and limit both freedom and happiness. Therefore, pirates > ninjas!

 
January 11, 2010 12:00 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

CloudDB update

The ComputeristSolutions site has been running with CloudDb for several weeks now and its going well. I don’t even think about it mostly, unless I want to change some content using the CloudDB web interface. Our implementation checks for content from clouddb for each page, and falls back to local content if its not available. The exception is the purely dynamic pages, which are based on pages in a certain path like this one.

The csinc site is built on Castle Monorail, an MVC framework.  The base controller defines a method to look up whether dynamic content is available and then loads it. Otherwise the method processes normally and the local file is shown. To handle the dynamic pages, I added a route that translates anything looking like /pages/{pagename}.ashx to /content/page.ashx?name={pagename}. Then either the dynamic content is show or a 404 page if no content available.

I previous covered how we access clouddb data. Here is some sample code for handling the dynamic pages:

Routing:

<monorail smtpHost="xxx" useWindsorIntegration="false">
		<controllers>
			<assembly>xxx</assembly>
		</controllers>
		<viewEngines viewPathRoot="Views">
			<add xhtml="false" type="Castle.MonoRail.Framework.Views.
                        NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity"/>
		</viewEngines>
		<routing>
			<rule>
				<pattern>^(/pages/)(\w+)(.ashx)*$</pattern>
				<!--pattern>^(/pages/sample.ashx)(.)*$</pattern-->
				<replace><![CDATA[ /content/page.ashx?name=$2 ]]></replace>
			</rule>
		</routing>
	</monorail>

 

Content controller

public class ContentController : BaseController
	{
		public void Page()
		{
			string contentName = Request.QueryString["name"];
			if (contentName == null || contentName.Trim().Length == 0)
			{
				Show404();
				return;
			}

			//show content or render blank; will have to change this to throw a 404
			if (!ShowDynamicContent(contentName))
				Show404();
		}
	}

That’s it; pretty easy. I’d recommend clouddb although I’m still feeling out what situations it’s best for. It’s certainly working out great as a lite CMS for our site.

 
January 7, 2010 10:35 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Ruby-Rails: vendor and client companies on a single model

I’m posting this mostly for my self to remember. It’s really not hard, but worth remembering. I have a model that has two foreign key fields to the company model. I wanted to expose this using nice ActiveRecord has_one .. :as => ‘xxx’ or something but doesn’t seem to be provide what I was looking for.  I wanted model.vendor to get the company marked as the vendor; and same thing for model.client.  Ultimately I went with the obvious manual way, but I’m happy with it.

class MySecretModel < ActiveRecord::Base
  belongs_to :user
  has_many :entries, :order => :on_date
  has_many :approval_requests
  # has_one :company, :foreign_key => :vendor_company_id, :as => :vendor
  # has_one :company, :foreign_key => :client_company_id, :as => :client

  def vendor
    Company.find_by_id vendor_company_id
  end
  
  def client
    Company.find_by_id client_company_id
  end

end
 
January 4, 2010 13:04 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Tips for Lazy Coders: Remove Friction

The less friction you have to coding something, the more likely you are to complete it. Reducing friction is a common theme for me. It’s relevant to maintaining an app, and building your own app. I have known plenty of people who have an idea – plenty of coders, but few who actually do it. Why? Because of friction, which is mostly within your control and probably partly yourself. Reduce friction, and achieve.

 
December 29, 2009 23:44 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Sample Rails code

This is for someone else.  I’m including some comments for explanation, but the target reader(s) will understand.  Sorry, folks, it was more readable than email.

from Entries controller – gets appropriate entries, plus start and end date.

   1:  class EntriesController < ApplicationController
   2:    before_filter :login_required
   3:    
   4:    def index
   5:      
   6:      @week_num = 0
   7:      if !params[:week].blank?
   8:        @week_num = params[:week].to_i
   9:      end
  10:      
  11:      @entries, @begin_date, @end_date = current_user.entries.find_for_week @week_num
  12:      
  13:      respond_to do |format|
  14:        format.html
  15:        format.js {render :layout => false, :partial => "list", :locals => { :entries => @entries, :begin_date => @begin_date, :end_date => @end_date }}
  16:      end
  17:    end
  18:   
  19:  ...
  20:   
  21:  end

 

Entry model method – does the dirty work of getting the records. returns records plus dates.

   1:  class Entry < ActiveRecord::Base
   2:    belongs_to :user
   3:    
   4:    def self.find_for_week(num)
   5:      # finds weeks in reverse order. 0 is current week. 1 is last week, 2 is 2 weeks ago. etc.
   6:      #assume monday is first day of week
   7:      t_now = Time.now - (7*24*60*60 * num)
   8:      t_wday = t_now.wday==0 ? 6 : t_now.wday-1
   9:      t_mon_begin = t_now - (t_wday)*24*60*60 - t_now.hour*60*60 - t_now.min*60 - t_now.sec
  10:      t_sun_end = t_mon_begin + 7*24*60*60 - 1
  11:      entries = find_all_by_on_date(t_mon_begin..t_sun_end, :order => 'on_date DESC') # ..finds all within range.
  12:      return entries, t_mon_begin, t_sun_end
  13:    end
  14:    
  15:  end

 

index.html.erb – renders the header, new link, and partial with list. update div is for the ajax call.

   1:   
   2:  <h2>Time Entries</h2>
   3:  <p><%= link_to 'new', new_entry_path, :class=>'applink', :id => 'new' %></p>
   4:   
   5:  <div id="update">
   6:  <%= render :partial => "list", :locals => { :entries => @entries } %>
   7:  </div>

 

_list.html.erb – renders links for ajax navigation, and list of records.

   1:  <% if @week_num > 0 %>
   2:  <%= link_to 'Next Week', '/entries?week=' + (@week_num.to_i - 1).to_s, :class => "get"%>
   3:  <% end %>
   4:  <%= link_to 'Prev Week', '/entries?week=' + (@week_num.to_i + 1).to_s, :class => "get"%>
   5:  <input type='hidden' value='<%= @week_num.to_i + 1 %>' id='weeknumber' />
   6:   
   7:  <% if flash[:notice] or flash[:message] or flash[:error] %>
   8:          <div id="flash" class="flash_html">
   9:            <%= flash[:notice] unless flash[:notice].blank?%>
  10:            <%= flash[:error] unless flash[:error].blank?%>
  11:            <%= flash[:message] unless flash[:message].blank?%>
  12:          </div>
  13:  <% end %>
  14:  <br/>
  15:   
  16:  <p> For week starting on: <%= @begin_date.strftime('%a, %b %d %Y') %></p>
  17:  <table cellpadding='3px' cellspacing='3px' border='0px'>
  18:  <tr>
  19:    <th width="30px"></th>
  20:    <th width="100px">Action</th>
  21:    <th width="100px">Date</th>
  22:    <th width="60px">Hours</th>
  23:    <th width="150px">Project Code</th>
  24:    <th width="200px">Description</th>
  25:  </tr>
  26:  <%= render :partial => "entry_row", :collection => @entries %>
  27:  </table>

 

_entry_row.html.erb – renders the actual row data

   1:  <tr>
   2:    <td><%= entry_row_counter+1 %></td>
   3:    <td><%= link_to 'edit', edit_entry_path(entry_row) %> | <%= link_to("delete", {:action => "destroy", :id => entry_row}, :confirm => "Are you sure you want to delete this entry?", :method => :delete) %></td>
   4:    <td><%=h entry_row.on_date.strftime('%b %d %Y') %></td>
   5:    <td><%=h entry_row.hours %></td>
   6:    <td><%=h entry_row.code %></td>
   7:    <td><%=h entry_row.description %></td>
   8:  </tr>

 

from application.js – jquery javascript used for ajax call, updating html, and setting click method on certain links

   1:  jQuery.fn.getWithAjax = function() {
   2:    this.unbind('click', false);
   3:    this.click(function() {
   4:      //$.get($(this).attr("href"), $(this).serialize(), null, "script");
   5:      $.ajax({
   6:        url: $(this).attr("href"), 
   7:        type: 'get', 
   8:        dataType: 'html',
   9:        success: function(msg){
  10:          $('#update').html(msg);
  11:          ajaxLinks();
  12:          }
  13:      });
  14:      return false;
  15:    })
  16:    return this;
  17:  };
  18:   
  19:  //This will "ajaxify" the links
  20:  function ajaxLinks(){
  21:      $('.ajaxForm').submitWithAjax();
  22:      $('a.get').getWithAjax();
  23:      $('a.post').postWithAjax();
  24:      $('a.put').putWithAjax();
  25:      $('a.delete').deleteWithAjax();
  26:  }
  27:   
  28:  $(document).ready(function() {
  29:    //default click event for all links
  30:    $('#applink').unbind('click', false);
  31:    $('#applink').click(function() {
  32:      self.location = $(this).attr("href");
  33:      return false;
  34:    })
  35:   
  36:    $.ajaxSettings.accepts.html = $.ajaxSettings.accepts.script;
  37:   
  38:    $(document).ajaxSend(function(event, request, settings) {
  39:         if (typeof(window.AUTH_TOKEN) == "undefined") return;
  40:         // <acronym title="Internet Explorer 6">IE6</acronym> fix for http://dev.jquery.com/ticket/3155
  41:         if (settings.type == 'GET' || settings.type == 'get') return;
  42:         settings.data = settings.data || "";
  43:         settings.data += (settings.data ? "&" : "") + "authenticity_token=" + encodeURIComponent(window.AUTH_TOKEN);
  44:       });
  45:       
  46:    $(document).ajaxSend(function(event, request, settings) {
  47:      if ( settings.type == 'post' ) {
  48:          settings.data = (settings.data ? settings.data + "&" : "")
  49:              + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN );
  50:      }
  51:    });
  52:    ajaxLinks();
  53:    
  54:    $(document).bind('keyup', 'Ctrl+l', function(){ self.location = "/login"; } );
  55:    $(document).bind('keyup', 'Ctrl+r', function(){ self.location = "/register"; } );
  56:    $(document).bind('keyup', 'Ctrl+e', function(){ self.location = "/entries"; } );
  57:    $(document).bind('keyup', 'Ctrl+o', function(){ self.location = "/logout"; } );
  58:    $(document).bind('keyup', 'Ctrl+n', function(){ 
  59:      //alert($('a#new').length);
  60:      if($('a#new').length) {
  61:        //$('a.new').trigger('click');
  62:        $('a#new').click();
  63:      }
  64:    } );
  65:  });

 

 

screenshot – looks like this because a good css and design haven’t been added.  this is after navigating back a couple weeks; notice the url shows it wasn’t just navigating pages.

Screen shot 2009-12-16 at 10.53.27 PM

 
December 16, 2009 23:05 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Its not that hard blog engine

I started thinking, again, about writing my own blog engine ever since Rob Conery threw out the challenge.  He’s followed through on that himself too.  My excuses are pretty simple and real: Don’t have the time and have too much other non-paid work already.  Still, I keep thinking about it because I’m not completely satisfied with blogengine.net.  It’s a fine blog engine, but it’s doesn’t integrate well with the rest of this site.  Also, I’d like to see something handled differently; more that I could do by contributing to the project.

The thing is, it’s not really that hard to build a simple blog engine.  As with anything, I’m sure it takes a little more to make it really good, but it’s just not that hard.

Think about it.. what entities do you need assuming this is database driven? Users, Posts, Comments, Categories, and some settings.  You’d probably want a built in post editor; there’s also the fairly simple Metaweblog api which would let you support Windows Live Writer.  I’d suggest supporting future posts; that’s just checking the publish date before showing a post.  Support saving a post as a draft is simple too.  It only gets more complicated with extra features like full-text searching, or roles for multiple authors.

So if someone just started a project, used FluentMigrator to manage the schema, and Fluent-NHibernate to wire up the models.. well.. you’re a fair ways to creating your own blog engine.  Oh, and use Free Text Box for the built-in html editor to make it easy.  I’m not necessarily going to do this; just thinking out loud.

 
December 16, 2009 10:44 by josh
E-mail | Permalink | Comments (2) | Comment RSSRSS comment feed

CloudDB v3 api

Update

So I got the csinc site switched over to using v3 api instead of the v2 CloudDB strongly typed service. Generally, I like it because it allows greater flexibility. That could have been true for v2 api, but I haven’t tried it so I can’t say. The CloudDB hasn’t put together the docs for this yet, so I’ll show you some code to use it below.

Why use it

First, it keeps the website portable. It doesn’t matter where it’s hosted because the datastore doesn’t change. Second, We can tweak content and even some behavior/layout stuff easily by making the change using the clouddb website.

Implementation

I’m not entirely settled with our implementation so far. How we read columns and set object properties needs work. Also, I re-factored the base to pass in a context object basically just to pass around a pointer to the IWindsorContainer instance so it would work in the web app and in the test project. It’s one of those quick hacks which you’re not quite happy with but don’t really have another good idea at the time. I just wanted my tests to pass.

Getting started

First you have to get an invite, and then set up a clouddb account.  Once done, add the clouddb api url as a webreference and begin coding. Start with the login code:

   1:  public static Token Login(string user, string password)
   2:  {
   3:    using (CloudDB cdb = new CloudDB())
   4:    {
   5:      return Login(cdb, user, password);
   6:    }
   7:  }
   8:   
   9:  public static Token Login(CloudDB cdb, string user, string password)
  10:  {
  11:    Token tok = cdb.LogIn(user, password).Result;
  12:    return tok;
  13:  }

Using an instance of CloudDB and the Token, you call whatever api method you need. Our site only uses reads so far.  So here’s how we get a list of rows:

   1:  public static RowsResponseData GetRowData(CloudDB cdb, Token token, string appName, string entityName, Predicate[] predicates)
   2:  {
   3:    App app = cdb.GetApplicationByName(token, token.UserID, appName, false, false).Result;
   4:    Entity e = cdb.GetEntityByName(token, app, entityName, false).Result;
   5:    RowsResponseData rowData = cdb.SearchEntity(token, app, e, predicates, 0, 0, 0, 0).Results;
   6:    return rowData;
   7:  }

 

In the above code, the Predicate array is used to specify which rows to get back from clouddb. That’s how we get a single match if we want.  Just query for the id or other unique criteria. Here’s how we handle that in our record base class:

   1:  public static T GetRecord(IAppContext context, string cacheKey, string entityName, Predicate[] predicates)
   2:  {
   3:    CachedItem ci = DataCache.GetItem(cacheKey);
   4:    if (ci != null) return (T)ci.Data;
   5:   
   6:    try
   7:    {
   8:      ICloudConnectionSettings connectionSettings = context.Container.Resolve<ICloudConnectionSettings>();
   9:      RowsResponseData rowData = CloudDBDataAccess.GetRowData(connectionSettings.CloudDBApplicationName, entityName, connectionSettings.CloudDBUser, connectionSettings.CloudDBPassword, predicates);
  10:      T item = new T();
  11:      item.Init(rowData.Data, 0);
  12:      DataCache.Add(cacheKey, item);
  13:      return item;
  14:    }
  15:    catch (Exception er)
  16:    {
  17:      T item = default(T);
  18:      DataCache.Add(cacheKey, item);
  19:      return item;
  20:    }
  21:  }

 

You’ll notice that caching is also handled at that layer so the specific model doesn’t have to know or think about it. (Yes, there are things I can do to improve this. Remember, time for our own site isn’t a high priority versus time for clients.)

..and a sample usage:

   1:  protected bool ShowDynamicContent()
   2:  {
   3:    return ShowDynamicContent(Request.FilePath.ToLower());
   4:  }
   5:   
   6:  protected bool ShowDynamicContent(string contentName)
   7:  {
   8:    var pageContent = PageContent.GetByPath(new AppContext(), contentName);
   9:    if (pageContent != null && pageContent.IsPublished)
  10:    {
  11:      PropertyBag["contentTitle"] = pageContent.Title;
  12:      PropertyBag["contentDescription"] = pageContent.Description;
  13:      PropertyBag["content"] = pageContent.Content;
  14:      RenderView("../shared/contentview");
  15:      return true;
  16:    }
  17:    return false;
  18:  }
 

   1:  public void Index()
   2:  {
   3:    //show dynamic content if available, defaults to view file content if not
   4:    ShowDynamicContent();
   5:  }

 

Good to know

Clouddb data is described with Entity objects representing the table, Column objects describing the columns for data, and Row objects containing the data. Think of it a little like a DataSet in .Net. You’ll need to know a little about the columns in the data, and then read the values from the Rows array. Also, you’'ll need to know the application name, which is the specific clouddb database in your account.

That’s a simple overview. Hopefully it helps. There is more to it, but that should get you started.

kick it on DotNetKicks.com

 
December 10, 2009 07:00 by josh
E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

about josh

another programmer blogging about his misadventures in writing code.

Contact

contact us for website & software consulting

Decide

decide on pragmatic solutions

Develop

develop your product together

Succeed

achieve your goals with our services