It's been a while since my last post. I've been hacking along though. Here's an interesting puzzle I faced recently.
In the app, I have an model called Corporation. Each corporation has a reporting frequency, a start date and the timezone it's in. The task is to calculate the next (and the last) reporting date for each corporation. It seems pretty straight forward, but the devil is in the details ...
The trimmed down version of my implementation is below
class Corporation < ActiveRecord::Base
REPORTING_FREQUENCY = %w(monthly bi-monthly quarterly half-yearly yearly)
REPORTING_FREQUENCY_INCREMENTS = { 'monthly' => 1.month,
'bi-monthly' => 2.months,
'quarterly' => 3.months,
'half-yearly' => 6.months,
'yearly' => 12.months }
def next_reporting_date
now = Time.now.in_time_zone(time_zone).to_date
return start_date if start_date > now
reporting_date(now, start_date.day, start_date)
end
private
def reporting_date(now, original_start_day, start)
if original_start_day > start.day && start.end_of_month.day >= original_start_day
start = start + (original_start_day - start.day).days
end
return start if start >= now
reporting_date(now, original_start_day, start + reporting_frequency_increment_value)
end
def reporting_frequency_increment_value
REPORTING_FREQUENCY_INCREMENTS.fetch(reporting_frequency)
end
end
The only tricky bit is the first if block inside the recursive reporting_date call. It makes sure we're not losing days when we cycle through months that have less days than the start_date.day value.
Hope I'm making sense here ... and I'm sure there are existing algorithms out there doing what I want more elegantly.
Start of the year, some fun stuff.
I recently had some spare time and re-visited my first ever uni assignment. The problem it's trying to solve is easy, given any amount of money in cents, figure out the minimium number of coins required. No need to say, the little Java program I wrote was crazily bad. So I rewrote it again in Ruby.
And here's my solution after over 10 years of programming practice :)
module ChangeMachine
class IdiotEncountered < StandardError; end
AVAILABLE_UNITS = [10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5]
def self.show_me_the_money(dollar_in_cents)
raise IdiotEncountered, "You are an idiot, no such amount in AUS..." unless dollar_in_cents % 5 == 0
dispense(dollar_in_cents)
end
def self.dispense(dollar_in_cents)
return {} if dollar_in_cents == 0
unit = AVAILABLE_UNITS.select{|u| u <= dollar_in_cents}.max
{unit => (dollar_in_cents / unit)}.merge(dispense(dollar_in_cents % unit))
end
end
puts ChangeMachine.show_me_the_money(37500).inspect
# => {10000=>3, 5000=>1, 2000=>1, 500=>1}
My Java version can be found here under the comments section.
I'm making a fresh OSX installation. After installing PostgreSQL using homebrew, the service cannot startup intermittently. The PostgreSQL server log says
FATAL: could not create shared memory segment: Cannot allocate memory
DETAIL: Failed system call was shmget(key=5432001, size=3809280, 03600).
HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory or swap space, or exceeded your kernel's SHMALL parameter. You can either reduce the request size or reconfigure the kernel with larger SHMALL. To reduce the request size (currently 3809280 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.
The PostgreSQL documentation contains more information about shared memory configuration.
Straight forward stuff, modify or create /etc/sysctl.conf and toss in the following
kern.sysv.shmmax=16777216
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.sysv.shmall=65536
Apparently I have done this before from my previous OSX installations... Next time will be easier with this note jotted down here.
The other night, I watched Yehuda's talk, Ember.js, State of the Union. One thing Yehuda said strikes me. He briefly talked about framework's internal modularity and how a framework should be presented to developers.
There's no doubt a good framework needs to be modular, so that changes can be easily made and new features can be added in relatively easily. I see 2 types of modular framework out there.
- Modular frameworks that are branded to be opinionated, a.k.a, Rails. Despite its modular internals, from the outside, you use the framework as instructed, no more, no less. Out of the box, it solves a typical type of problem. However once you become confident and efficient with it, you have the power to ride the Rails with your own way, thanks to its modularity.
- Frameworks that are modular, and the framework provides the modules as building blocks. Things are then left to the devs to work out, a.k.a. Zend Framework. It's super flexible, but it's hard to figure out how to handle the beast. You need to do all the glue work yourself. Put the pieces together yourself. The steep learning curve often leads to mis-use, and yes that's talking from self-experience.
So what makes a good framework? Back to what Yehuda said, it's gotta be modular, with an easy and default way to interact with the framework. The framework needs to set sensible defaults out of the box.
Rails does this really well. Maybe one day I'll outgrow Rails, but for now, I can't be happier.
To back me up, just compare how Zend Framework and Rails handle building web forms (yes, that's what we do on the web all the time...)
What one is better? You are the judge!
Chrome is my primary browser because it's fast and minimal. One thing surprised me today is its (in)ability to deal with content caches.
The issue I encountered is this.
- I have a resource
myapp.com/my_resource.
- I first load the page in with a request of content type text/html. The page itself has some Javascript, which will issue an Ajax request to the same URL
myapp.com/my_resource. The Ajax request is requesting the same resource URL, but with a different content type, application/json.
- On the
my_resource page, I click on another URL
- Once I'm on the new page, I hit the back button(I'm lying here, I press the
delete key). Chrome dumps out all the JSON data returned by the Ajax request instead of rendering the previous "text/html" page content.
The reason is when Chrome caches content, it discards HTTP headers. This means as long as the resource URL is the same, the Ajax request's response automatically becomes the cache value for my myapp.com/my_resource URL. To me, this really breaks RESTful web apps' state management.
Problem is tricky, fix is easy. Just toss in a random number as a postfix to the Ajax request URL (well, it's not really a solution. The Ajax request will not get cached by the browser anymore...)
$.getJSON "/my_resource?#{new Date().getTime()}", (data) ->
# blah blah
EDIT
Since I'm working on a Rails app, instead of hacking a timestamp to the Ajax URL, I can just make the URL .json
$.getJSON "/my_resource.json", (data) ->
# blah blah
While I'm drafting this post, I got pointed to this article, Fear Of The Class: Fat Model Kills Your Software Design, from a tweet. This article says 90% of the things I wanted to say here, so I'll not repeat those.
One extra thing I want to point out is some madness I faced in recent weeks. And of course, it relates to the AR model responsibilities topic.
In Rails land, I accept the fact we can call to_json on a AR model instance and pump the output back as a JSON response. I occasionally use it when I'm just prototyping something, but I seriously believe calling to_json on AR model is wrong. to_json simply isn't a responsibility to an AR model class. Handle it in the views with RABL, use a decorator/presenter, freedom is all yours!
As I dislike Rails giving all AR model to_json method, I'm hit by something much worse. In a central ORM lib at work, a recent change was made to automatically serialise boolean fields to JSON format upon data retrieval. So if someone needs just a true or false ... too bad, ORM gives back JSON true and JSON false, convert it back to boolean yourself!!! When I read and noticed the change, it was a WTF moment. When I questioned the change and told to go and read on it, I wave white flag ...
While writing an integration test, I needed to pass a custom HTTP header to the GET request. Doing a quick scan in the Rails source (I'm using Rails 3.1.3) points me to here.
It looks like all I need is to include my custom HTTP header in a hash and pass it to the get call as the last argument.
Fairly straight forward, I came up with this
get "/api/xxxxx/xxxxx/xxxxx",
{ format: "json" },
{ authorization: "Token token=\"#{app.token}\"" }
As you can see, I trusted the doc, I used authorization as the header hash key. I was hoping it'll be turned into HTTP_AUTHORIZATION. And you can guess, it didn't, my request spec was failing badly, returning me HTTP 401 instead, not passing the initial authentication. It turned out that the Rails doc is lying to me. I had to fire the get call like this instead
get "/api/xxxxx/xxxxx/xxxxx",
{ format: "json" },
{ "HTTP_AUTHORIZATION" => "Token token=\"#{app.token}\"" }
One reason I really enjoy working with Ruby/Rails is because of the Gems. When you want to integrate a Rails app with some 3rd party utilities, it's pretty likely that you can find existing Gems doing just that for you (80-20 rule here of course).
I need to integrate CKEditor with my Rails app. Guess what? There's a ckeditor gem for it! Just follow the VERY good readme to get it installed. For a Rails 3.2 app, you literally just need to add 2 lines or 3 lines of code to have it all set up ...
Add it to your Gemfile, and bundle install it
If you want to handle uploads from the editor, stick the following into your ruotes.rb. I didn't do this myself, because I'm only after text editing.
mount Ckeditor::Engine => "/ckeditor"
Next, in your app's javascript manifest file, add the following line
//= require ckeditor/init
At this point, it's done and working (this is the 80% rule)! However for what I needed (the 20%), I did some customisation.
I'm using the awesome simple_form, so I created a little helper method in my application_helper.rb
def rich_text_editor(form, attr)
form.input(attr, as: :ckeditor, input_html: { class: 'string', customConfig: asset_path('ckeditor_config.js') }).html_safe
end
As you can see, I load a custom ckeditor_config.js. It basically gives me a custom tool bar. Oh! Don't forget to add ckeditor_config.js to config.assets.precompile list in application.rb.
CKEDITOR.editorConfig = function( config )
{
config.toolbar = 'MyToolbar';
config.toolbar_MyToolbar =
[
['Bold', 'Italic','Underline', '-', 'NumberedList', 'BulletedList'],
['UIColor','TextColor','BGColor'],
['Link', 'Unlink','Anchor'],
['Maximize', 'ShowBlocks','-','Source'],
['Paste','PasteText','PasteFromWord','-','SpellChecker']
];
};
Now, in any of my view files, I use the helper method like this
= simple_form_for @object do |f|
= rich_text_editor f, :description
All good! Yes, all good on development... Deploy it to staging, it fails miserably. Why? Asset Pipeline again! For Heroku hosted apps (staging and production), I'm using an S3 asset_host for static assets. The CKEditor init script tries to load it from the app's domain instead...
After reading the ckeditor init script, it's very easy to fix! Rename the javascript manifest file to application.js.erb, so that we can use Ruby code. Make the manifest require self and add some voodoo to set the CKEDITOR_BASEPATH.
//= require_self
// ...
// your other requires, make sure require_self comes before requiring ckeditor/init
//= require ckeditor/init
(function() {
<% if Rails.env.production? %>
window['CKEDITOR_BASEPATH'] = "http://<%= ENV['FOG_DIRECTORY'] %>.s3.amazonaws.com" + "/assets/ckeditor/";
<% end %>
}).call(this);
That's it! Why I use ENV['FOG_DIRECTORY'] in the basepath? See my previous posts(1 and 2) for details.
Sweet, all good.
Quick and short one. The Heroku hosted app I'm working on has a staging env. I need to quickly deploy from local to the staging Heroku remote repo a lot. Doing git push staging whateverbranch:master is fine, but just tedious to type every time. And since the app is at early development stage, I need to constantly deploy to staging from a local feature branch to just do the show-n-tell, a simple bash/zsh alias will not do the job (well, I don't know, maybe it can ...).
So... since I'm using RVM and a project .rvmrc anyway, I throw the following function into my project .rvmrc. With that, I can just do deploy staging, job done!
deploy() {
local ref=$(git symbolic-ref HEAD 2> /dev/null) || return
local current_branch=${ref#refs/heads/}
echo "Deploying to Heroku: "
echo " - git push $1 $current_branch:master"
git push $1 $current_branch:master
echo "Running database migration: "
echo " - heroku run rake db:migrate"
heroku run rake db:migrate
}
It's quick and dirty, and once the app gets released I'm sure I'll come back to modify it or move the deployment into a Rake task. Working just fine for now!
This is a follow up post to yesterday's article on how I work with asset pipeline for a Heroku hosted app.
While the setup described from the previous article works. It leaves a few sticking points.
- Before I do assets precompile, I have to either
export the necessary environment variables manually, or set them up in my .zshrc globally.
- It's hard to precompile assets for different deployment targets, e.g. staging and production.
- I have to remember to run the assets clean rake task to keep my local dev env sane.
So ... I did some follow up work. The README I wrote for the specific project is as follow.
Asset Compiling
Assets are precompiled locally and automatically uploaded to S3 with the help from the asset_sync gem.
After step into the project root directory, simply calling a custom ap() function, such as
to compile the assets and upload the compiled assets to S3.
For this to work, RVM on local development machine is required. The handy function ap() is defined in .rvmrc under the project root.
The argument passed into the ap() function should be a valid Heroku Git remote name.
For example, we have staging setup on Heroku because of the following git remote output.
⚡ git remote -v
origin git@bitbucket.org:xxxxx/xxxxx.git (fetch)
origin git@bitbucket.org:xxxxx/xxxxx.git (push)
staging git@heroku.com:xxxxx.git (fetch)
staging git@heroku.com:xxxxx.git (push)
This means we will need to create a .projectrc.staging file under the project root. A projectrc template is provided, .projectrc.sample. 3 config variables need to be filled in.
Note that FOG_DIRECTORY is the Amazon S3 bucket name. To mute fog's noisy output during assets precompile, name the S3 bucket using only alphabets and . is recommended.
The same 3 variables are also set on Heroku and can be found by doing
The ap() function does the following
- precompile assets locally
- upload the compiled assets under
/public/assets to the corresponding S3 buckets
- purge local compiled assets from
/public/assets, with the exception of /public/assets/manifest.xml, which is pushed to Heroku.
Now to finish it up, here's my .rvmrc
rvm ruby-1.9.2-p290@my_project --create
ap() {
local projectrc=".projectrc.$1"
if [ -f "$projectrc" ]; then
source "$projectrc"
bundle exec rake assets:precompile
git add public/assets/manifest.yml -f
git commit -m 'updated asset manifest'
bundle exec rake assets:clean && git checkout public/assets/manifest.yml
else
echo "Missing '$projectrc'. Check README for details."
fi
}
And here's the .projectrc.sample
export FOG_DIRECTORY=XXXXXXXXXXXXX
export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXX
Hope this makes sense and helps.