Darian Shimy

Technical Blog

Everything I Forget About Git

Inspecting

View commits by a user:

1
git log --author="Darian Shimy"

View file commit history:

1
git log -- path/to/filename.ext

Find commits with deleted files:

1
git log --diff-filter=D --summary

Find all deleted files:

1
git log --pretty=format: --name-only --diff-filter=D | sort -u

Show which files were modified for a commit:

1
git show --stat sha

View the first parent commit of a commit:

1
git show sha~1

Send file contents from a commit to STDOUT:

1
git show sha:path/to/filename.ext

View commits that are not in another branch:

1
git log --not master

Fixing Mistakes

Recover a deleted file from a long time ago:

1
2
3
4
5
# Find the SHA of the last commit
git log -n 1 --oneline -- path/to/filename.ext

# Don't forget the ~1 to get the commit that had the file:
git show sha~1:path/to/filename.ext

Fix a commit message:

1
git commit --amend

Add a file to the last commit:

1
2
git add missing_filename.ext
git commit --amend

Revert a non-pushed commit:

1
git reset --hard sha

Revert a pushed commit:

1
git revert sha

Unstage a file:

1
git reset HEAD path/to/filename.ext

Revert changes to an unmodified file:

1
git checkout -- path/to/filename.ext

Branches

Delete a local branch:

1
git branch -d branch-name

Delete a remote branch:

1
git push origin :name-of-deleted-branch

Create a tracking branch:

1
git checkout -t origin/branch-name

Create a branch and push it to the remote:

1
2
3
git checkout -b branch-name
...
git push -u origin branch-name

Managing Autocomplete

Autocomplete can be disabled in the HTML by setting the autocomplete attribute to off. In INPUT elements:

1
<input type="password" name="password" autocomplete="off">

In FORM elements:

1
2
3
<form name="new_session" method="post" action="#" autocomplete="off">
...
</form>

Using JavaScript:

1
$('#element_id').attr('autocomplete', 'off');

Standups Are NOT Poisonous

Gareth Rees’ article titled, Standups are Poisonous hit HN today which I strongly disagree with every point and his solution. Almost to the point where I couldn’t fall back asleep and felt compelled to write something. The purpose of our standups are to do the following:

  • Make sure everyone is working on the right thing
  • Help out other team members by taking work off their plate or helping them by sharing domain-specific knowledge
  • Keeping everyone informed

Easy. It’s important to point out, they are NOT status meetings. Status meetings suck and should be avoided. Gareth made the following arguments (which I respond as follows):

Morning standups force people to be in work before 10:00. Great when you’re supposed to have the benefit of flexi-time.

We don’t have any set working hours. Some come in at 8:00 and some at 12:00. Our standups start at 1:31 every day. No need to set your alarm clock. This time is perfect since people have finished lunch, had time to play a few games of ping pong, and are ready to get back to work. We chose one minute past 1:30 since everyone was late to the 1:30 meeting. WFH that day, no problem, we have a Google Hangout for you to tap into. Have a hot date during that time, no worries, shoot over an email. Super busy that day, fuck it, I’ll catch up with you tomorrow.

They always overrun. Rarely are standups shorter than 10 minutes. 6 person team * 30 minutes = 3 hours lost.

Our meetings NEVER go over the time limit. Period. I’m not sure why people have this problem. The person running the meeting needs to cut of sidebar conversations and take long running discussions offline.

Action points are rarely produced, so the value of the outcome is questionable.

The goal of the meeting is to eliminate action points (or impediments). If this is the case, your project may be progressing as it should.

Others switch off if they’re not interested in the current monologue.

When you build a talented team, they show respect to other team members by paying attention. Perhaps people switch off when the meeting hits the three hour mark.

Notes are rarely taken, so by the time the weekly update gets compiled the team have to scratch their heads about what they did over the last week.

I don’t take notes unless I need to follow up on something. Notes are not needed as this is very transient information. It has no value 24 hours past the meeting.


The root of the problem is he feels standups are status meetings, they’re not! The solution calls for an email system where a team leader compiles daily and weekly updates from the team. This has its own problems:

  • The leader needs to hound the team to get their update (doesn’t sound like a fun job)
  • No one will read the daily email (or weekly for that matter)
  • Information is stale once things get sorted out (you lost a day)
  • There is no emotion carried with this process (seeing someone’s stress level and realizing they need help does a long way)
  • You should care about what was done, not worked on (there is a difference)

Standups are not poisonous or evil. They are a great tool when run correctly. Remember, they are not status meetings.

Webfont HTTP Access Control and Firefox

With Jabwire, I am hosting the font assets in Amazon’s CDN, Cloudfront. Everything worked as expected with Chrome and Safari, but Firefox was not rendering the fonts. It was downloading the file, but showing the unicode box instead of the font characters. Turns out I was running into a problem with Cross-Origin Resource Sharing (CORS). CORS is a W3C proposal for enabling secure cross-site data transfers.

Here is much more information than is needed on this subject: https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS

After adding the required HTTP header to the Apache configuration, everything worked as expected.

1
2
3
4
5
<IfModule mod_headers.c>
  <FilesMatch "\.(ttf|ttc|otf|fnt|eot|woff)$">
    Header set Access-Control-Allow-Origin "*"
  </FilesMatch> 
</IfModule>

Scoped IDs in Rails

Rails by default will use the auto-increment value for the ID which is global to the application. Sometimes, you want to have the IDs scoped to a parent object. For example, if I were building a multi-user bug tracking application I would want to have the IDs scoped to the project allowing each bug ID to start at 1 (one). As it turns out, this is fairly easy to do.

First create a column on the parent object to keep track of the next scoped_id:

1
2
3
4
5
6
7
8
9
class CreateProjects < ActiveRecord::Migration
  def change
    create_table :projects do |t|
      ...
      t.integer :bugs_sequence
      ...
    end
  end
end

Next, create a column to store the scoped_id in the child object:

1
2
3
4
5
6
7
8
9
10
11
12
class CreateBugs < ActiveRecord::Migration
  def change
    create_table :bugs do |t|
      ...
      t.references :project
      t.integer :scoped_id
      ...
    end
  end
  add_index :bugs, :project_id
  add_index :bugs, [:project_id, :scoped_id]
end

Finally, update the Bugs model to support the scoped ID:

1
2
3
4
5
6
7
8
9
10
before_create :populate_scoped_id

def populate_scoped_id
  project.increment!(:bugs_sequence)
  self[:scoped_id] = project.bugs_sequence
end

def to_param
  self.scoped_id
end

That’s pretty much it. No gem needed here. When you are ready to access the bug by the scoped_id, just find it from the project:

1
@project.bugs.find_by_scoped_id(params[:id])

Rails Callbacks Are Better Than Accessors

There are a few problems with overriding a model’s default accessors:

  • There are multiple ways to set an attribute without calling the accessor, e.g., write_attribute. Because of this, you are not guaranteed to have this code executed which can result in bad data.
  • There is not a good way to set associations in the accessor. The write_attribute method doesn’t handle this case without some nasty hacks.
  • It pollutes the model and slows development since you don’t really know what the accessor is doing.

You can achieve a similar result with callbacks. For example, using a before_save callback, you can easily add code to make any needed data manipulations. If the cases where you only want to make a change when the attribute was updated, you can use the attributename_changed? method. Remember to look for the id of the changed model.

Anti-pattern:

1
2
3
4
5
6
7
8
9
10
11
12
class Issue < ActiveRecord::Base

  def project=(p)
    write_attribute :project_id, p  # doesn't work

    # Code that does not belong
    if p.security(:private)
      p.audit_log :change, self
    end
  end

end

Improved version:

1
2
3
4
5
6
7
8
9
10
11
class Issue < ActiveRecord::Base

  before_save :audit_log

  def audit_log
    if self.project_id_changed? && self.project.security(:private)
      self.project.audit_log :change, self
    end
  end

end

Texatar: Texual Avatar

Texatar is a server that generates images with text to replace default gravatar image.

The problem we face is not everyone has a gravatar image linked to their email address. When we show images in place of names, they all look the same and there is no way to differentiate between people. Texatar will generate an image based on text for the user. The color scheme is equally important and is based on a hash of the user’s email.

Usage

Images are generate from http://texatar.jabwire.com/:size/:text.png

The following parameters used to control the generated image:

  • :text = the text to display in the texatar
  • :size = the size of the image in pixels

For example, the link, http://texatar.jabwire.com/200/ds.png, would create a 200 x 200 image with the text ds in the center.

Texatar

Gravatar Integration

Gravatar allows developers to supply their own default image with the d= or default= parameter. Be sure to URL encode the parameter’s value.

1
<img src="http://www.gravatar.com/avatar/00000000000000000000000000000000?d=http%3A%2F%2Ftexatar.jabwire.com%2F200%f2ds.png">

See https://en.gravatar.com/site/implement/images/#default-image for more information.

JavaScript iPhone 4/5 Detection

Safari returns the same user agent is you are using an iPhone 4/4s or iPhone5. The only useful information is which version of iOS the user was running.

1
Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25

Instead, you can use JavaScript to determine which phone by looking at the height. Note: the CSS resolution is half the actual resolution due to the higher dpi of the retina display. I’m sure this will break in the future, but it seems to work for now.

1
2
var iphone4 = (window.screen.height == (960 / 2));
var iphone5 = (window.screen.height == (1136 / 2));

Disable Rails Asset Generators

Sometimes you don’t want to generate the associated stylesheet or javascript files when creating a controller or scaffold. This can be turned off via the command line with the ‘–skip-assets’ options or you can add the following lines to your ‘config/application.rb’ file.

1
2
config.generators.stylesheets = false
config.generators.javascripts = false

iOS 6 App Banners

iOS 6 provides an easy way to notify users of a native iOS app using a feature called Smart App Banners. Smart App Banners are displayed when the following tag is added to a page:

1
<meta name="apple-itunes-app" content="app-id=myAppStoreID">

The app-id is a nine digit unique identifer for the app. This is the graphic displayed when the app is not installed:

This is the graphic displayed when the app is installed: