Link
Link

I wanted to try my hand at some html css art. So here is my Marvin the Martian which I loved to draw as a kid.

Text

Javascript debouncer

Sometimes when attaching callback functions to event you want to make sure that the event doesn’t fire a whole bunch of times while the event is occurring consistently. A technique to stop this from happening is debouncing. Debouncing is actually a term borrowed from electronics that keeps a circuit from firing more then once. To do this in js use the following function

//Makes sure an event is not still occuring before firing the callback
//for example, if a window is being resized it may call the resize event multiple
// times
function debouncer( func , timeout ) {
  var timeoutID , timeout = timeout || 200;
  return function () {
    var scope = this , args = arguments;
    clearTimeout( timeoutID );
    timeoutID = setTimeout( function () {
      func.apply( scope , Array.prototype.slice.call( args ) );
    } , timeout );
  };
}

You can then add it to a function call like so

$(window).resize(debouncer(function() {
  $(‘.centerme’).centerMe();
}, 200));

Text

A simple rails state machine

So I need functionality like a state machine but all the state machines out there where way more then what I need. Essentially I just need to be able to send emails when the status of a case switches from one state to another. Turns out active record has a pretty great little tool to assist with this called Dirty. http://apidock.com/rails/ActiveRecord/Dirty

Now before you jump to some Miley Cyrus joke here is what it does.

You can check what a attribute_was, if attribute_changed? and a before and after array with status_change. These brilliantly named method make building a simple state transition checker rather straight forward.

1. create your function checking the change.

def starting_work?
  if self.status_change == [“new”, “in_progress”]
    return true
  else
    return false
  end
end

2. Then you can use the method as a condition on a callback function:

after_save :email_warranty_assigned, if: :starting_work?

3. Profit.

In it’s purist form this is not a proper state machine as it does not prevent things from transferring to the wrong state. But that was functionality I did not require.

Text

Active Admin, semantic_form_for, and has_many

I have been working with Active Admin on my latest project and one of the requirements that lead to me choosing Active Admin was the ability to create custom forms. Let me begin by saying for any basic content manipulation active admin is great. It has a DSL built on top of formtastic for building custom forms in the admin. The combination of this and the boilerplate admin setup really speeds up development. However there are some gotchas along the way. Currently I have been dealing with one and I thought I would document what I have found so far.

In this particular scenario I was building out a custom form from a partial. I want the ability to have swf upload move data to a temp s3 bucket and add records to a has many relationship. So what I am building is as follows:

1. When creating a new Marketing Media item the user uploads many files to a temp s3 bucket.

2. When each file upload completes a js callback adds a new relationship of type Advertising Entity to the currently selected Marketing Media. Allowing for multiple files to be added.

3. When saved the Marketing Media entity saves each of the related Advertising Entity elements which sets up a queue to move the items to a final url on s3, this url is being maintained by paperclip.

Ok this is a fairly unique set of form requirements. All of the queuing and transferring of files is being taken care of by the models and rails delayed job.

Marketing Media model:

class MarketingMedia < ActiveRecord::Base

  validates :title, presence: true, length: { maximum: 140 }
  validates :body, presence: true

  has_attached_file :image, styles: {
    small: “150x150>”,
    medium: “300x300>”,
    large: “600x600>”
  }

  has_many :advertising_entities, dependent: :destroy
  accepts_nested_attributes_for :advertising_entities,
    :reject_if => lambda { |attributes| attributes[:file_file_name].blank? },
    :allow_destroy => true
  acts_as_ordered_taggable
end

Advertising Entity Model:

require ‘aws’


class AdvertisingEntity < ActiveRecord::Base
  belongs_to :marketing_media
  include Expirable
  acts_as_ordered_taggable

  has_attached_file :file

  validates :body, presence: true
  validates :title, presence: true, length: { maximum: 140 }

  # setup the needed file attributes for paperclip
  before_create :set_upload_attributes
  # setup delayed job to move the file.
  after_create :queue_processing

  # Set attachment attributes from the direct upload
  # @note Retry logic handles S3 “eventual consistency” lag.
  # this sets all the things that paperclip needs to work with an
  # attachement.
  # pass in s3 so it can be mocked out.
  def set_upload_attributes(s3=AWS::S3.new(:access_key_id =>       ENV[‘AWS_ACCESS_KEY_ID’],
  :secret_access_key => ENV[‘AWS_SECRET_ACCESS_KEY’]) )
    return true unless file_file_name != “”
    # amazon fails try multiple times
    tries ||= 5
    # get our file
    direct_upload_head = s3.buckets[ENV[‘AWS_TEMP_BUCKET’]].
    objects[file_file_name].head
    #set up all the file information
    self.file_file_name = file_file_name
    self.file_file_size = direct_upload_head.content_length
    self.file_content_type = direct_upload_head.content_type
    self.file_updated_at = direct_upload_head.last_modified

    # retry logic
    rescue AWS::S3::Errors::NoSuchKey => e
      tries -= 1
    if tries > 0
      sleep(3)
      retry
    else
      true
    end
  end

  # Delayed job tha asynchronously moves the media image to its final   destination and
  # deletes the temp file. based on:
  # http://blog.littleblimp.com/post/53942611764/direct-uploads-to-s3-with- rails-paperclip-and
  # pass in s3 so it can be mocked out.
  def self.transfer_and_cleanup(id, s3 = AWS::S3.new(:access_key_id =>      ENV[‘AWS_ACCESS_KEY_ID’],
  :secret_access_key => ENV[‘AWS_SECRET_ACCESS_KEY’]) )

    #get the asset to manipulate
    document = AdvertisingEntity.find(id)

    #setup the final file path… we need to get rid of the first slash or it wont move properly.
    paperclip_file_path = document.file.path[1..-1]

    #copy the s3 file from its temp position to its final position
    bucket1 = s3.buckets[ENV[‘AWS_TEMP_BUCKET’]]
    bucket2 = s3.buckets[ENV[‘AWS_BUCKET’]]
    obj1 = bucket1.objects[document.file_file_name]
    obj2 = bucket2.objects[paperclip_file_path]

    response = obj1.copy_to(obj2, :acl => :public_read)

    # flag it as moved
    document.processed = true
    # save the changes
    document.save

    #delete the temp file.
    obj1.delete
  end

  # Queue file processing
  def queue_processing
    AdvertisingEntity.delay.transfer_and_cleanup(id)
  end
end

The trouble spot. After working with the form builder a while I was running into a number of problems creating this form. Their is some well documented oddness with the way the Active Admin Form Builder DSL and the Formtastic DSL work together. It seems that in some cases the form will be drawn twice. Lets take a look at my code and see what has led to my solution so far.

<div id=”main_content”>
<%= semantic_form_for [:admin, @marketing_media],
:url => admin_marketing_medium_path(@marketing_media), builder: ActiveAdmin::FormBuilder do |f| %>
<% f.semantic_errors :state %>
<% f.inputs :name => “Description” do %>
<% f.inputs :title %>
<% f.inputs :body %>
<% f.input :tag_list, :hint => ‘Comma separated’ %>
<% f.has_many :advertising_entities, :heading => ‘media’,
:allow_destroy => true do |p| %>
<% p.input :id, as: :hidden %>
<% p.inputs :title %>
<%= p.inputs :body %>
<%= p.input :temp_url, as: “hidden”, id: “temp_url” %>
<%= p.input :file_file_name, as: “hidden”, id: “file_name” %>
<% end %>
<% f.actions :commit %>
<% end %>
<% end %>

<div class=”uploader”>
<h1>Uploader</h1>
<div id=”file_lists”>
<ul id=”file_done_list” class=”file_list”></ul>
<ul id=”file_todo_list” class=”file_list”></ul>
</div>

<div id=”queue_size”></div>

<div id=”overall”><span class=”progress”><span class=”amount”>10%</span></span><span id=”status”></span></div>

<% raw s3_swf_upload_tag(
file_size_limit: “1000MB”,
file_upload_limit: 5,
queueSizeLimit: 1,
:onFileNotInQueue => “alert(‘File not found in the queue’);”,
:onFileSizeLimitReached => “alert(‘That file is too big’);”,
:onQueueChange => “queueChangeHandler(queue);”,
:onQueueSizeLimitReached => “alert(‘There are too many files in the queue’);”,
:onQueueEmpty => “alert(‘You gotta have at least one file in there’);”,
:onQueueClear => “queueClearHandler(queue);”,
:onUploadingStart => “uploadingStartHandler();”,
:onUploadingFinish => “uploadingFinishHandler();”,
:onSignatureIOError => “alert(‘There was an error’);”,
:onSignatureXMLError => “alert(‘There was an error’);”,
:onSignatureSecurityError => “alert(‘There was an error’);”,
:onUploadError => “alert(‘There was an error’);”,
:onUploadIOError => “alert(‘There was an error’);”,
:onUploadSecurityError => “alert(‘There was an error’);”,
:onUploadProgress => “progressHandler(progress_event);”,
:onUploadComplete => “uploadCompleteHandler(upload_options,event);”
) %>
<button id=”uploadmedia”>Start Uploading</button>
</div>
</div>
<%= javascript_include_tag “swf_upload_config” %>

Some note worthy things here.

1. Only the semantic_form_for tag has a = sign in the erb call. (well almost more on this later) It seems that if you put in the = sign for each and every block you run into duplicate rendering issues. For some reason their are two calls for each input area. This issue is talked about a lot on the github issues area for active admin 

https://github.com/gregbell/active_admin/issues/2401

https://github.com/gregbell/active_admin/issues/2189

https://github.com/gregbell/active_admin/issues/333

2. The custom html comes after the form. This one really bothers me, the whole reason I choose active admin was for custom html. However if you put custom html in with the form builder code it will do a few different things, it may display no content at all, it may duplicate content or it may show only some content. I have not dug in enough to understand the triggers foreach of these.

3. Their are not any empty lines in the form builder code. This is also a little annoying, it seems if you put empty lines in the wrong place then nothing will display. 

4. Inside the has_many block issue one seems to go haywire. For some reason the first two elements <% p.input :id, as: :hidden %>, <% p.inputs :title %> will display twice if there is an = but the other elements in this block will not display at all. 

I hope this helps some one some where with active admin forms. I am finding experimentation is the best path forward as currently my understanding of the three form builders (FormBuilder < ::Formtastic::FormBuilder < ActionView::Helpers::FormBuilder) work together is complicated at best.

I am continuing to use active admin as it help with a lot of boilerplate but sometimes it is weird. 

Text

Setting up a rails 4 application quick self reference

Here is a quick outline I put together for my self to remind my self how to setup a new rails project.

  1. cd to directory.
  2. rails new [app name] -T #no test suite flag
  3. Create RVM files .ruby-gemset, name in file and .ruby-version, version of ruby to use
  4. Setup basic gems
    source 'https://rubygems.org'
    ruby '2.0.0'
    # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
    gem 'rails', '4.0.0'
    gem 'pg', '0.15.1'
    
    group :development, :test do
    gem 'rspec-rails', '2.13.1'
    end
    
    group :test do
    gem 'selenium-webdriver', '2.35.1'
    gem 'capybara', '2.1.0'
    gem 'guard-rspec', '2.5.0'
    gem 'spork-rails', '4.0.0'
    gem 'guard-spork', '1.5.0'
    gem 'childprocess', '0.3.9'
    gem 'factory_girl_rails', '4.2.1'
    gem "launchy", "~> 2.1.2"
    end
    
    gem 'sass-rails', '4.0.0'
    gem 'uglifier', '2.1.1'
    gem 'coffee-rails', '4.0.0'
    gem 'jquery-rails', '3.0.4'
    gem 'turbolinks', '1.1.1'
    gem 'jbuilder', '1.0.2'
    
    group :doc do
    gem 'sdoc', '0.3.20', require: false
    end
    
    group :production do
    gem 'rails_12factor', '0.0.2'
    end
    
  5. bundle install
  6. Setup config->database.yml
  7. rake db:create
  8. rake db:migrate
  9. rails g rspec:install
  10. Setup guard and spork:
  11. http://ruby.railstutorial.org/chapters/static-pages#sec-guard
  12. Add factory girl file spec->factories.rb
  13. Setup focused rspec tests: spec->spec_helper.rb
    RSpec.configure do |config|
    .
    .
    .
    config.mock_with :rspec
    config.filter_run :focused => true
    config.alias_example_to :fit, :focused => true
    config.run_all_when_everything_filtered = true
    .
    .
    
Text

Expression Engine on Pagoda Box

Picking a hosting environment is always a tricky task. There are so many options these days it is hard to know what to choose. For my latest Expression Engine projects I have been trying Pagodabox and have been happy with the choice.

Pagodabox is a PAAS (platform as a service) php hosting environment. This is nice because they let you scale each piece of the service independently, want to add more servers just slide a slider in the web interface and a new instance is spun up and load balanced. Pagodabox also offers the ability to push changes to the site with a git workflow, and maintains all branches and version. If you need to tweak any of the PHP servers setting they have a yml config file (.boxfile) that will load allowed libraries and setup other php settings.

The Pagoda gem is another super handy feature. This tool lets you check log files, tunnel into databases, and even scale from the command line. Finally they offer SSH connections to shared directories to make it easy to get into certain parts of the file system.

Getting setup is pretty easy, I actually followed a handy tutorial by The Good Lab  this got me up and running. I then setup automatic DB backups to s3 using this script 

The thing I struggled with the most is the shared writable directory setup. I kept running into little bugs like captchas not showing up. This was an issue with my shared dir setup every time. Make sure you upload all required writable directories and that they are set to 777. What I did was to ssh into the shared file and check to see if things where there if not I either made them with the command line or copied them over. 

Above and beyond this I would also point out that it is a great place to host your site because you can get a lot for free, this is really beneficial to me as I have been able to setup dev environments for each site I am building with no cost and I can tunnel the production database and the dev database to each other to sync changes.

Text

A manifesto

This morning I was thinking about what my web design manifesto would be, here is what I came up with.

I believe in websites that get out of the way of users and showcase information, products or capabilities. Websites are canvases used for delivering objects, or enabling actions. As a web developer/UX preacher it is my job to organize those canvases so you can find, understand and use these canvases. Whenever possible to do this I try to gain empathy for user needs, present solutions to problems and constantly test these hypothesis until the largest possible audience is pleased.

Text

The UX Process of marinbikes.com

Building a new type of website is always difficult for me, I find it very easy to stick within the patterns of what i know and love. But each type of website has it’s own Zeitgeist to consider. Coming from years of doing news websites doing a marketing website introduced a new way of thinking that I had not previously been in the brain space of. To help me out of my common ways of thinking I under took a lot of research.

Designing product marketing sites is very different from news sites in someways and very similar in others. In order to understand the consumer mentality when buying a bike I did what a customer would do. I went and pretended to buy bikes. Disclaimer I am a cyclist so this was only useful when dealing with consumers who knew what they where buying. This process helped me to understand the way good sales people talk about bikes and what all needed to be talked about. 

Since I know a lot about bikes I also spend a good amount of time shadowing, creepily in some cases, other people who where buying and selling bikes. From this I drew up some personas and users needs. Three main personas are the expert (which I fall into), the enthusiast, and the novice. Each have different levels of product knowledge and differing levels of knowing what they want.

This along with a great book I was reading at the time Convert lead to a information architecture and navigation that allowed for novice to go to what I am calling definition pages and experts to bypass these pages and get closer to bikes that interest them.

Seeing as many people shopping for bikes will also shop our competitors sites I also wanted to stay inside the users expectations so that if they learn how to navigate our competition’s site (which they will likely visit before our site) then they will also know how to navigate our site and will not need to learn new navigating techniques.

From here we went through all the common steps of wire framing, some testing (although admittedly not enough) and development. In a future blog post I will talk a bit about development. 

Following trends and learning buying processes really informed the UX of the new Marin Bikes website. Hopefully this has led to a much better user experience early stats from a survey are showing 90% of users saying that the site is moderately to very easy to navigate which is encouraging and many people are saying they love the big images and smooth sleek layout. Hopefully it is well liked, oh and I hope you digg the new bikes I think they are beautiful.

Text

MADUX

This weekend I attended Madison UX for the first time. It is surprising that I had not attended it before, being from Madison and all. Madison UX is a relatively small conference with one track and quite a range of speakers. 

In retrospect the conference was not quite what I had expected, but was quite useful regardless. There was a good mix of informative applicable discussions and inspirational outside of the box talks. The pacing of this varied content was quite good also, keeping you entertained and engaged for two days with no self selection can be a challenge.

Some of my favorite talks included:

Jenn Downs (Mailchimp) “Empathy cannot be automated”

In this talk Jenn pulled from seemingly long boring hours of reading tons of spam and telling us what not to do. This was great as I would never want to wade through enough spam to get to a point where I thought about what actually worked. But she has done the leg work to follow tons (is there a digital unit for ton?) of email marketers in an effort to find those diamonds in the rough. 

Lesson learned - Don’t be annoying or stalky unless you have a compelling reason and sometimes a human touch although expensive can add more values then an equation. Music was the best case scenario here, I personally quite like services that make suggestion about what music I may like, computers can do some of this but are limited by there audience and if the audience becomes completely reliant on the system no new recommendations will ever occur.

 

Martin Atkins - “Kickstarter”

You can sell the F word.

 

Pamela Pavliscak - “Why UX Needs Numbers”

I found this talk right up my alley. I have a weird computer/business background which seemingly conflicts with UI thinking some times. I personally love UI/UX and would like to be able to do it full time however some times I feel that the science of UI/UX falls too much into the qualitative space and does not bring enough quantitative measurements to the table. I definitely agree with this and was very happy to see it be evangelized at this conference. Pamela did a good job of introducing why it is a useful techniques and gave some solid example of how to use different types of data sets for different stake holder depending on their level of perceived geekdom.   This topic could cover an entire conference but it was encouraging to see a discussion being had.

 

There where many other great talks, and free ice cream. But these where my standout talks for this years MadUX. DId you attend? What was your favorite talk?