Hexo file name escape issue and solution

I’m working on reorganize posts today, and found some filename issue with Hexo.

Speical symbols are not escaped properly

Just found this issue today.

To ensure filename generate from title is legal as either file name or url path, Hexo uses hexo.util.escape.filename to escape the illegal symbols. filename does escape some symbols, but it doesn’t cover all illegal symbols, such as +, =, &, etc. If these symbols are not escaped properly, which generates illegal url link. As a result, your post will never be acessible.

I found this issue is because, I have a post with +3 trainer in its title, since + is not escaped, and + will be treated as space by http. As a consequence, my post will never be able to open, unless I escape + as %2B. To avoid this kind of problem, I wish all the symbols in post name are escaped as -.

Besides this issue, Hexo has another issue with name escaping that the escaped file name might contains continues -. For example, your post title is “A great introduction - part 1”, you will get escaped name a-great-introduction---part-1.md. I wish the continues - in the file name should be replaced with single -, name a-great-introduction-part-1.md is more readable than previous one.

To fix the 2 issues, I just created a pull request, hope it will be merged soon.

I cared about this issue so much is because I uses hexo-consle-rename plug-in, which also uses util.escape to handle file name. To keep naming consistency when between different version of Hexo, I addd a kind of evil monkey patch in the v0.1.2. So make sure the plug in can work properly even with old Hexo.

File name case issue

Besides the Hexo naming issue, I also met the case of the filename today. Although it depends on the blog hosting, but it might cause 404 if the file name case changed.

To avoid this kind of issue, I strongly recommend to set filename_case: 1 in _config.yml, which will make sure all file name are in lower case.

There is common pitfall here for Windows or Mac with case-insensitive file system. If you have deploy the website once with wrong filename casing. Regenerate after updated filename_case won’t help, because file system won’t treat case change in filename as “change”. So you cannot really commit the “change” to fix the 404 issue.

To fix this issue, there is easy and efficient trick. Go to .deploy folder, execute following commands:

1
2
3
4
$ git rm -rf *
$ git ci -m "Clean all file"
$ hexo clean
$ hexo d -g

To force clean the repo once enforce git to treat 2 files with same name but different casing as different ones. So the name casing issue can be fixed.

converting between HTML 5 data-attribute style hyphen name and javascript camcel-case name

I found a bug in widget.coffee today. To fix the issue, I need the conversion between HTML 5 data-attribute name and javascript function name, e.g. conversion between data-action-handler and actionHandler.

By taking jQuery implementation as reference, I come up 2 utility functions for the conversion:

NameConversion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Utils =
hyphenToCamelCase: (hyphen) -> # Convert 'action-handler' to 'actionHandler'
hyphen.replace /-([a-z])/g, (match) ->
match[1].toUppercase()
camelCaseToHyphen: (camelCase) -> # Convert 'actionHandler' to 'action-handler'
camelCase.replace(/[A-Z]/g, '-$1').toLowerCase()
attributeToCamelCase: (attribute) -> # Convert 'data-action-handler' or 'action-handler' to 'actionHandler'
Utils.hyphenToCamelCase dataAttribute.replace(/^(data-)?(.*)/, '$2')
camelCaseToAttribute: (camelCase) -> # Convert 'actionHanlder' to 'data-action-handler'
'data-' + Utils.camelCaseToHyphen(camelCase)

Here is a more solid implementation based on previous one.

a sloid javascript version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var Utils = (function() {
function hyphenToCamelCase(hyphen) {
return hyphen.replace(/-([a-z])/g, function(match) {
return match[1].toUppercase();
});
}
function camelCaseToHyphen(camelCase) {
return camelCase.replace(/[A-Z]/g, '-$1').toLowerCase();
}
function attributeToCamelCase(attribute) {
return hyphenToCamelCase(dataAttribute.replace(/^(data-)?(.*)/, '$2'));
}
function camelCaseToAttribute(camelCase) {
return 'data-' + camelCaseToHyphen(camelCase);
}
return {
hyphenToCamelCase: hyphenToCamelCase,
camelCaseToHyphen: camelCaseToHyphen,
attributeToCamelCase: attributeToCamelCase,
camelCaseToAttribute: camelCaseToAttribute
};
})();

A pitfall in jQuery form serialization

Today, I was so surprised that I got an empty string when I call the serialize method on a jQuery wrapped form.
The html is written in Haml:

Html
1
2
3
4
5
6
7
%form#graph-option.horizontal-form
%fieldset
%label{ :for=>'start-date'} Start Date
%select#start-date
%label{ :for=>'end-date'} End Date
%select#end-date
%button#submit-option.btn.large-btn

And the script is written in coffee-script:

Script
1
2
3
4
5
6
7
$ ->
$('#submit-option').click ->
option = $('#graph-option').serialize()
$.post '/dashboard/graph', option, (data) ->
renderCharts data

When I execute the script, I got 500 error. And the reason is that the option is empty.
I believe this must be caused by a super silly mistake, so I try to call serialize methods on Twitter Bootstrap website, and I still got empty string!!!!

After half an hour debugging, I just realize that I forgot to assign the name to all the input elements. And according to html specification, the browser uses the name of the elements to identify whom the value belongs to.
So when the name is omitted, the serailizeArray method in jQuery returns an empty array, as a result, the serialize method returns empty string.

According to my experience, it is easy to identify this problem, if the html is in html-like format, such as erb. But it is really hard to identify this issue if the page is written in haml, because in haml, id is used much more often.
To fix this problem, we need to specify the name explicitly for each form element.

Here is the fixed haml code:

Html
1
2
3
4
5
6
7
%form#graph-option.horizontal-form
%fieldset
%label{ :for=>'start-date'} Start Date
%select#start-date{ :name=>'start-date' }
%label{ :for=>'end-date'} End Date
%select#end-date{ :name=>'end-date' }
%button#submit-option.btn.large-btn

Name trap in Rails

I’m a newbie to Rails, and my past few projects are all rails based, including MetaPaas, Recruiting On Rails and current SFP.
I was amazed by the convenience of Rails, and also hurt by its “smartness”.
The power of Rails was described in quite a lot of posts, so I wanna to share some failure experiences.
Actually I have felt into quite a number of pitfalls in Rails, and here is one of the most painful ones.

To explain the problem easier, I just simplify the scenario:
I have a model called “Candidate”, which holds a “Status” to store the status of the candidate, so I have the code like this:

Candidate and Status
1
2
3
4
5
6
7
8
9
10
11
class Candidate < ActiveRecord::Base
has_one status
# other definition
end
class Status < ActiveRecord::Base
belongs_to candidate
# other definition
end

For some reason, I change the relationship between Candidate and Status. It is changed from one-to-one to one-to-many.
So I changed the has_one to has_many:

Candidate and Status
1
2
3
4
5
6
7
8
9
10
11
class Candidate < ActiveRecord::Base
has_many status
# other definition
end
class Status < ActiveRecord::Base
belongs_to candidate
# other definition
end

I thought it is an easy modification, but the app fails to run, even I have done the database migration.
It said rails cannot find a constant named “Statu”!

After my first sight on this error message, I believed it is caused by a typo, I must mistyped “Status” as “Statu”.
So I full-text search the whole project for “Statu”, but I cannot find any.

This error message is quite weird to me, since I have no idea about where the word “Statu” come from!
After spent half an hour on pointless trying, I suddenly noticed that the word “status” is end with “s”, and according to Rails’ convention, rails must think “status” is the plural form of “statu”. So according to the convention again, it try to find a class named “Statu”.
And we should use the plural form noun as name for one-to-many field, since that holds an array rather than a single object.

So after changing status to statuses, the problem solved.

Convention based system is powerful, a lot magic just happened there. But also the magic things are hard to debug when some special case breaks the convention presumption.