Hack hack hack...

An open journal-- some of it written for you, but most of it is for me.

Service Objects Versus Concerns

I wrote this last May. Obviously, @scottcreynolds doesn’t think it sunk in.

We were writing a URL sanitizer that stripped the user’s URL input of http:// and www to store in the DB. Because we were using it for multiple models. I had viewed this “stipper” method as a behavior and thus had thought it belonged in the the DHH blessed concerns folder.

Scott disagreed:

A concern or mixin should describe a behavior that is being given to the object. That’s the “able” nomenclature. A behavior is something like “it can support tags” or “it can be authenticated with a devise token”.

What I should have seen was this is a utility method. He writes:

What was being created here is a utility or service method, intended to be used across any entity in the system that chooses to sanitize a url. This is not a behavior that the object has. Yes, you could go so far as to say “this object can sanitize its urls” but that’s a bit of semantic gymnastics when what we’re really describing is a service object - something that provides a service to any part of the system that requires it. In this case, scrubbing user input.

Something that may have helped me futher understand this was thinking about who was responsible for this sanitization. Is the Event object responsible for url sanitization?

It might seem like a fine hair to split, but if we examine further, we see that we weren’t really adding a common behavior to the event and admin_user classes.

The parseable module as designed only handled the case where the thing it was being mixed into had a field called website. Including it in event would have done absolutely nothing because that field doesn’t exist. What an event does have, however, is a registration_link. Already, we can see that we haven’t truly identified a common behavior so much as a common need - hence a service object.

Finally, just in the naming we can tell that we’re not really describing what we mean. If a class includes a module parseable, that include line, in English, reads an admin user is parseable. To me that means something significantly different than before save, strip some characters out of a field. The module being described didn’t do what it claimed to do by its name.

These things seem small, but they’re extremely important to care about because code bases grow and all you have in a dynamic language is your tests and your naming to help you understand the system a year from now. I could show you a rails project I just came into where no care was given to naming and structure, and you could spend days following the labyrinth of modules and classes and end up hoping the Minotaur finds you and eats you and spares you the hassle of figuring out what’s going on.

So in short, use concerns when extracting common behavior (active record finders, authentication, etc). Use service objects when extracting common needs.

Finally, a note on tests. It’s great we’re doing the cucumber, and doing it well, but we still need tests at model and controller levels too. If you make a new class/module it should have a test file and some tests.

Pow Config

I had a bunch of issues getting pow up and running after I upgraded to Mavricks. I ended up not installing via the curl method that the 37signals suggests as it wasn’t recognizing my rbenv and was running on my system ruby. So I uninstalled and tried it with homebrew, which required a different path in my .powconfig file. Just like the troubleshooter said it would via the github issue, this worked.

Final code: export PATH="/usr/local/opt/rbenv/shims:/usr/local/opt/rbenv/bin:$PATH"

Boom! Smack! Pow!

Cucumber With Scott

The point of BDD is to build a system form a perspective of a user, rather than just as a developer.

Login

As a partner, I need to be able to login, so that I can update my upcoming events.

Need to deliver a story and prove that it works.

An integration test is any test that cross the boundries of a system

Why cucumber

  • Gherkin language for feature files
  • rspec for tests
  • capybara to drive the browser

The tools are the same just when with gherkin thrown on top - cucumber is platform and language agnostic

So a problem I have is when to test built in features of the framework or gem.

Given, When, Then

  • same as Arrage, Act, Assert (which is what ALL tests do) in people talk

  • Arrage

    let(:something) {do_something}

1
2
3
before do
  arrange_things
end
  • Act, and Assert
1
2
3
4
it "does something when I do this" do
  do_a_thing #=> act
  expect(that_thing).to eq(something) #=> assert
end

Misc

  • user regex in the step file if you are going to reuse it
  • acceptance tests should be used to test large swatches rather than every permuation

tags

  • @pause like a binding
  • @wip won’t run unless specified to run
  • custom tags work too, so even then you’d run it with cucumber -t @jonas
  • or you can run multiple tags at one time, e.g. cucumber --tags @billing,@important

Cucumber

Cucumber

Cucumber is a BDD tool that supports Outside-in development by running plain text features or user stories as acceptance tests.

Features

Let’s create our first feature. The anatomy of a feature is the following:

1
2
3
4
Feature: [feature]
  In order to [business value]
  As a [role]
  I want to [some action]

Notice how a feature includes who, what and most importantly: why.

For example:

1
2
3
4
Feature: Manage Articles
  In order to make a blog
  As an author
  I want to create and manage articles

Scenarios

There will be many scenarios accompanying a feature. Scenarios will follow the four stages of testing: setup, exercise, verify and teardown. A simple scenario looks like this:

1
2
3
Given [context]
When I do [action]
Then I should see [outcome]
  • The Given step is where you set up the context of your scenario. Every scenario starts with a blank slate, so it is important to create a state in your application for example by creating data in the database, or by navigating to a specific page.
  • The When step is where you exercise the application in order to accomplish what needs testing. This is usually where you might fill in forms, press buttons, click links, or otherwise interact with the system in some way.
  • The Then step is where you verify the result, and it’s where we check that the correct pages are rendered, that we see a success or error message, or anything that could help us verify that the prior action was successful.
  • The teardown, is taken care of by cucumber automatically.

For example:

1
2
3
4
5
Scenario: Articles List
  Given I have articles titled Pizza, Breadsticks
  When I go to the list of articles
  Then I should see "Pizza"
  And I should see "Breadsticks"
1
2
3
4
5
6
7
8
9
Scenario: User signs up with valid data
  When I go to the sign up page
  And I fill in "Email" with "email@person.com"
  And I fill in "Username" with "user"
  And I fill in "Password" with "password"
  And I fill in "Confirm password" with "password"
  And I press "Sign up"
  Then I should see "instructions for confirming"
  And a confirmation message should be sent to "email@person.com"

We can also test a user attempting to sign up without filling in all the fields:

1
2
3
4
5
6
7
Scenario: User tries to sign up without a twiddr name
  When I go to the sign up page
  And I fill in "Email" with "email@person.com"
  And I fill in "Password" with "password"
  And I fill in "Confirm password" with "password"
  And I press "Sign up"
  Then the "Username" field should have the "can't be blank" error

Tags

cucumber --tags @billing --tags @important -> Runs the first scenario (Scenarios with @important AND @billing) cucumber --tags @billing,@important -> Runs both scenarios (Scenarios with @important OR @billing)

Wit

.ruby-version & rbenv

  • so rbenv now does support a ruby version file but doesn’t support fuzzy matching. See.

Multipart = true

  • HTML forms provide two methods of encoding. The default is application/x-www-form-urlencoded, which is more or less the same as a query string on the end of the URL. The other, multipart/form-data, is a more complicated encoding but one which allows entire files to be included in the data. via

Devise

  • had trouble with validation requirements built into the validatable module and configuring it to allow a user to edit their profile without requiring a password, which involved destroying the session because the salted password had changed. I ended up ripping it out and going with a validates_presence_of :password, :on => :create instead.

Factory Girl Associations

From the docs it’s possible to set up associations within factories. If the factory name is the same as the association name, the factory name can be left out.

1
2
3
4
factory :post do
  # ...
  author
end

To not save the associated object, specify strategy: :build in the factory:

1
2
3
4
factory :post do
  # ...
  association :author, factory: :user, strategy: :build
end

This does not work; causes author_id to be nil

1
2
3
factory :post do
  # ...
  author strategy: :build

Staging assets broken

Racked my brain (and everyone elses) over this one… It ended up I hadn’t added the rails_12factor gem to staging though I had added that environment.

Capybara cheatsheet

Cheatsheet

Server TimeZone

  • had a feature that pulls events once they have passed. Heroku’s time zone is by default set to UTC. So I did two things:
    1. heroku config:add TZ=”America/New_York” as described here.
    2. Set config.time_zone = 'Eastern Time (US & Canada)' in application.rb as described in the rails docs.

Fragment Caching

  • Had an issue expiring the fragment cache. The issue was that when you cache the fragment in your view, a cache digest is being added to the cache key (views/all_available_releases/41cb0a928326986f35f41c52bb3d8352), but expire_fragment is not using the digest (views/all_available_releases).

  • By adding skip_digest: true to the cache call in the view it should prevent the digest from being used.

1
2
3
4
5
6
7
8
<% cache "all_available_releases", skip_digest: true do %>
 <% @releases.each do |release| %>
  <% cache(release) do %>
   <html code with>
   <%ruby code @release.name blah blah blah%>
  <%end%>
 <%end%>
<%end%>
  • Cache digests are only intended to be used with automatic cache expiration. If you need to manually expire cache keys then you can’t use cache digests.
  • Saved by stackoverflow

New Beginnings

It has been a little more than a year from when I started at Carrot Creative. It has been an exciting year in Carrot history and year in which I had some personal milestones. Carrot was the perfect place for me over the past year and I’m eternally grateful for the opportunity to work on some large scale using a variety of technologies.

I am excited to be returning to the Flatiron School, this time as an instructor. Working with eager students who want to change the world through code is an exciting proposition and one I ultimately couldn’t resist.

Marionette-cms

Jade template

<%- %> insert into DOM <%= %> html escapes?

Markdown

  • so apparently markdown doesn’t come standard? I installed marked as the markdown interpreter.
    • with an install with npm this gave immediate markdown functionality to the jade templates, but it was still required in the specific model in order to call markdown related functions on a string of markdown within the template helper.
  • I also need to translate the modifications made to the HTML on the page to be translated back into markdown to store them on the server. This required another plugin and I settled on html.md. The install was clean and worked smoothly from the start.

This in the view

  • something that was not immediately apparent was the fact that this has different meanings in the template helper versus onRender for example.

Node, Express, Backbone and Marionette – Sorting Out the Pieces

Express with Node

  • Express is a framework that takes Node from a barebones application and turns it into something that behaves more like the web servers we’re all used to working with.
  • Express feels like the sinatra of node.

Bower vs. npm

  • It was advised that bower is the front end dependencies package manager, where the npm is for the backend. see
  • You specify what dir your want to install the bower packages with a .bowerrc file which goes in the project root. Here are some examples of these types of files.
    • This tutorial was helpful to get up and running with require.js and bower

Mean stack

Asynchronous Module Definition

  • The AMD format comes from wanting a module format that was better than today’s “write a bunch of script tags with implicit dependencies that you have to manually order” and something that was easy to use directly in the browser. further reading
  • The standard loader is RequireJS, but in production you could use a replacement AMD loader called Almond. It is a smaller “shim” loader, providing the minimal AMD API footprint that includes loader plugin support. Some developers like to use the AMD API to code modular JavaScript, but after doing an optimized build, they do not want to include a full AMD loader like RequireJS, since they do not need all that functionality. Some use cases, like mobile, are very sensitive to file sizes. By including almond in the built file, there is no need for RequireJS. almond is around 1 kilobyte when minified with Closure Compiler and gzipped.

require.js

In roots

  • require ['jquery', 'fitvid', 'modal'], ($) -> is how I might require a non underscored file