edtsech's notes

CoffeeScript

What is it?

  • CoffeeScript is a little language that compiles into JavaScript.
  • The golden rule of CoffeeScript is: “It’s just JavaScript”.

More info in README

When I saw CoffeeScript first time it looks weird: ‘:’ instead of ‘=’ and other strange things. And I wasn’t good about that. But things change and opinions of people too. Now I have very positive opinion about CS not only because CS makes code less noisy, more structured, more readable and less buggy, mostly because ECMAScript and CoffeeScript evolve together. And it makes CoffeeScript not “Yet another language that compiles to JavaScript”. CS has big influence on future and present of ECMAScript, I think. Below I have some examples(it’s not actually examples, just some links to CS website) of features that will be provided in ES6(in future version of ECMAScript specification) and these features already implemented in CS, some of them was borrowed from CS. I think, it’s awesome because we can just use nice features NOW. Also CoffeeScript provides big portion of syntax sugar(short functions, optional return, ranges, additional operators, string interpolation and etc) and fixes some bad parts of JavaScript:

Fixes

  • Problems with global scope (each file wraps to anonymous function)
  • Problems with weak typization (CS internally always uses === operator)
  • Awkward semicolons (CS has indentation syntax)

Features

Some features that will be in ES6/ES.next/ES Harmony and that already exist in CoffeeScript:

Links

PaperTrail :ignore vs :skip

:skip attribute/key was released in PaperTrail 2.4.1 What are the differences between :ignore and :skip?

For example we have Article model with ignored title field and skipped file_upload field:

1
2
3
4
class Article < ActiveRecord::Base
  has_paper_trail :ignore => :title,
                  :skip   => :file_upload
end

Create empty article object, initial version’ll be created:

1
2
3
4
5
>> a = Article.create
>> Article
=> Article(id: integer, title: string, content: string, file_upload: string)
>> a.versions.count
=> 1

If we update ignored title attribute, version won’t be created. If we update non-ignored content column, version’ll be created and we’ll have stored changes of object in object_changes column that available through changeset attribute.

1
2
3
4
5
6
7
8
9
10
>> a.update_attributes :title => 'Title'
>> a.versions.count
=> 1
>> a.update_attributes :title => 'New Title', :content => 'Content'
>> a.versions.count
=> 2
>> a.versions.last.changeset
=> {"content"=>[nil, "Content"]}
>> a.versions.last.reify
=> #<Article id: 1, title: "Title", content: nil, abstract: nil, file_upload: nil>

As we see ignored title column not stored in changeset but it stored in dumped object. But there are some cases when we don’t need to store some columns in dump object by various reasons. For these cases :skip key has been created. :skip and :ignore work identically, but :skip doesn’t store data of skiped columns in object dump. That’s it.

Information about attributes tracking you can find in paper_trail PaperTrail README “Choosing Attributes To Monitor” on GitHub. Tests for these features are presented in first two contexts in this file. Issue about :skip on GitHub.

Peace!

Underscore.string 2.0 Release

Why 2.0?

In this version we moved Underscore.string library to separate namespace _.string for solve name conflicts with Underscore library. There are some functions that available in both libraries, for ex. include and reverse. In 1.1.6 and lower Underscore.string provided includes function, but we decided that two function include and (includes or includeString) in one namespace and with same functionality but one function for collections another for strings, it’s a little bit ugly.

Do we always need to write _.string?

Nope. Underscore.string provide exports function _.string.exports(), this function returns only non-conflict functions and we can mix in these functions to Underscore scope if you want.

1
2
3
4
5
6
7
8
9
10
11
_.mixin(_.string.exports());

// Access to Underscore.string and Underscore functions
_.include([1,2,3], 1)
_.trim('  asd  ')

// Access to conflict Underscore.string functions
_.string.include('foobar', 'bar')
// or
_.str.include('foobar', 'bar')
// "str" it's just alias for "string"

Problems

We lose two things for include and reverse methods from _.string:

  • Calls like _('foobar').include('bar') aren’t available;
  • Chaining isn’t available too.

But if you need this functionality you can create aliases for conflict functions which will be convnient for you.

1
2
3
4
5
6
7
_.mixin({
    includeString: _.str.include,
    reverseString: _.str.reverse
})

// Now wrapper calls and chaining are available.
_('foobar').chain().reverseString().includeString('rab').value()

Standalone Usage

If you are using Underscore.string without Underscore. You also have _.string namespace for it. And current version number you can find through VERSION constant _.string.VERSION. If you want you can just reassign _ variable with _.string

1
2
3
_ = _.string

_.VERSION // => 1.2.0

Node.js Installation

npm package

npm install underscore.string

Standalone usage:

1
var _s = require('underscore.string');

Integrate with Underscore.js:

I recommend you this way for integrating with Underscore.js:

1
2
3
4
5
6
7
8
9
10
var _  = require('underscore');

// Import Underscore.string to separate object, because there are conflict functions (include, reverse, contains)
_.str = require('underscore.string');

// Mix in non-conflict functions to Underscore namespace if you want
_.mixin(_.str.exports());

// All functions, include conflict, will be available through _.str object
_.str.include('Underscore.string', 'string'); // => true

Upgrade

In new version function includes has been removed, you should replace this function by _.str.include or create alias _.includes = _.str.include and all will work fine.

Nothing special.

Peace!

PaperTrail 2.2.7-2.4.0 Release Notes

0. Changeset

From PaperTrail 2.2.7 release you can find a new method of Version instances, called changeset. PaperTrail doesn’t have diffs mechanism inside, but now if you have object_changes column in versions table (it can be generated automatically if you install PaperTrail with --with-changes option) it will store Rails’ changes of dirty objects.

1
2
$ rails g paper_trail:install --with-changes
# or manually add `object_changes` column in your `versions` table
1
2
3
4
>> widget = Widget.create :name => 'Bob'
>> widget.versions.last.changeset                # {}
>> widget.update_attributes :name => 'Robert'
>> widget.versions.last.changeset                # {'name' => ['Bob', 'Robert']}

More information about Diffing Versions you can find in PaperTrail README on GitHub.

1. Flexibility in naming of methods

There are some situations when methods version and versions are already busy by other associations or smth. In this case we can change the names of these methods in our application (but sometimes it’s time-consuming) or we can configure these methods in PaperTrail like that:

1
2
has_paper_trail :versions => :paper_trail_versions,
                :version_name => :paper_trail_version

2. Add :on option

With this option we can configure what events we need to track. For example, we don’t need to track create events:

1
has_paper_trail :on => [:update, :destroy]

3. Without Versioning

In some cases some action/actions must be executed without versioning. Now PaperTrail has simple wrapper for this case:

has_paper_trail.rb
1
2
3
4
5
6
7
8
# Executes the given method or block without creating a new version.
def without_versioning(method = nil)
  paper_trail_was_enabled = self.paper_trail_enabled_for_model
  self.class.paper_trail_off
  method ? method.to_proc.call(self) : yield
ensure
  self.class.paper_trail_on if paper_trail_was_enabled
end

Usage:

1
2
3
4
5
6
7
# With method name
@widget.without_versioning :destroy

# or with block
@widget.without_versioning do
  @widget.update_attributes :name => 'Ford'
end

4. Attr Accessible

Now we need to use attr_accessible if we want to store some meta info in versions table. Example of meta information from PaperTrail README:

1
2
3
4
5
6
7
8
9
class Article < ActiveRecord::Base
  belongs_to :author
  has_paper_trail :meta => { :author_id  => Proc.new { |article| article.author_id },
                             :word_count => :count_words,
                             :answer     => 42 }
  def count_words
    153
  end
end

In this case author_id, word_count and answer are meta, and we need to have these columns in versions table. And also we need to add these attrs to attr_accessible like in this example:

config/initializers/paper_trail.rb
1
2
3
class Version < ActiveRecord::Base
  attr_accessible :author_id, :word_count, :answer
end

5. TravisCI

And from now, like all cool stuff, PaperTrail uses TravisCI.

Peace!