Stub/Mock Rails view helpers in RSpec

This was not as easy to find on the inter webs as it should be. Example If you want to stub out a view helper (view_helper_method_x) in your view spec. allow(view).to receive(:view_helper_method_x) { return_value_goes_here_as_usual } The spec should look something like bellow # encoding: UTF-8 require 'spec_helper' describe ‘home/view.html.erb’ do before(:each) do allow(view).to receive(:view_helper_method_x) { true } end it ’should do blah blah' do render expect(rendered).to_not have_css ‘.test_id' end end That’s all folks!

December 18, 2014

Testing Rails Controller Helper Methods Using RSpec

If you are new to this. First read this article about Anonymous Controller. That should answer 98% of your questions. However, if you run in to problems the following tid bits of information might help ;) Summary of Anonymous Controller (code stolen from https://www.relishapp.com/rspec/rspec-rails/v/3-0/docs/controller-specs/anonymous-controller) class ApplicationController < ActionController::Base class AccessDenied < StandardError; end rescue_from AccessDenied, :with => :access_deniedprivate def access_denied redirect_to "/401.html" end end require "rails_helper" RSpec.describe ApplicationController do controller do def index raise ApplicationController::AccessDenied end end describe "handling AccessDenied exceptions" do it "redirects to the /401.html page" do get :index expect(response).to redirect_to("/401.html") end end end Gotchas Missing helper methods If RSpec is having problems finding your helper methods within the Anonymous controller (it shouldn’t, because by default the controller should inherit from the controller you are testing), try the following. ...

December 17, 2014

Varnish cache purging from dynamically instantiated EC2 instances behind a ELB using HTTP header based authentication

We use a Varnish cache server to do basic page caching for our Rails apps. The application instances are automatically instantiated by a ASG(auto scale group) and all these instances sit behind a ELB. Usually Varnish cache purge requests are only allowed from a few white listed IPs. Like bellow: acl purge { "localhost"; "192.168.55.0"/24; } sub vcl_recv { # allow PURGE from localhost and 192.168.55... if (req.method == "PURGE") { if (!client.ip ~ purge) { return(synth(405,"Not allowed.")); } return (purge); } } However, because an ELB doesn’t have a static IP (it changes with time, depending on various factors) and also because our application instances themselves are dynamically instantiated and have no static IPs associated with them, we can’t simply use a white list to allow PURGE requests from our app servers. ...

November 6, 2014

npm ERR! cb() never called! (on npm 1.4~)

If you are getting the bellow error on a recent version npm (1.4~) npm ERR! cb() never called! npm ERR! not ok code 0 Chances are you are facing this issue: npm shrinkwrap install - npm ERR! cb() never called! (Linux / OS X) https://github.com/npm/npm/issues/5920 Unfortunently there is no fix avialable as of yet (29th Oct 2014). The only solution seems to be rolling back to a old version of npm unaffected by the bug. ...

October 29, 2014

Set Ansible Variables from the Command Line

If you ever wanted pass in ‘parameters’ to your Ansible playbooks via the command line when you are running the playbook through ansible-playbook here is the way to do it. Command line ansible-playbook --extra-vars '{"NODE_ENV":"staging","GIT_BRANCH":"develop"}' playbook.yml or the shorter version (thanks mpdehaan) ansible-playbook "-e 'NODE_ENV=staging GIT_BRANCH=develop'" playbook.yml Here you are setting NODE_ENV and GIT_BRANCH variables (if it wasn’t obvious). Now inside your playbook you will have something like the following - name: Node apps deploy hosts: all gather_facts: no vars: git_branch: "{{ GIT_BRANCH|default('develop') }}" node_env: "{{ NODE_ENV|default('staging') }}" tasks: - command echo "{{ node_env }}" - command echo "{{ git_branch }}" That’s about it. ...

October 24, 2014

Setting up Centralized Logging to S3 with logrotated

What we are trying to do here Get all logs to be backup to S3 whenever logrotated runs or whenever the machine is shutdown/restarted (so that if you have AWS auto scaling enabled, you won’t loose any logs). Basic Steps Install s3cmd Setup a script to upload logs to S3 (that’ll be called via logrotated) Setup a init.d script to run logrotated -f (forced) on shutdown/restart Setup logrotated scripts to call your S3 upload script Install s3cmd sudo apt-get install s3cmd setup s3cmd by running ...

August 27, 2014

Using Varnish (v4) as a reverse proxy cache with Rails

Setting up Varnish You can install varnish by following the official installation guide (this one is for Ubuntu, but you can find a official guide that supports your OS) at: https://www.varnish-cache.org/installation/ubuntu Make sure to edit /etc/default/varnish and change DEAMON_OPTS so that varnish listens on port 80 /etc/default/varnish DAEMON_OPTS="-a :80 \ -T localhost:6082 \ -f /etc/varnish/default.vcl \ -S /etc/varnish/secret \ -s malloc,256m" Caching only selected requests (white listing what to be cached) The following code will ensure that Varnish will only cache whatever that matches the filters define. Everything else will be passed to the backend. ...

August 22, 2014

Using MongoDB 2.6 with TravisCI Builds

At the time of writing this post TravisCI uses mongodb 2.4 (when you put in mongodb under services in .travis.yml) and does not support the new version 2.6 ‘out of the box’. However, if you do need 2.6 for your code to run properly you could easily install it your self. You can add the following to your before_install (in .travis.yml) to do that. .travis.yml before_install: - "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10" - "echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list" - "sudo apt-get update" - "sudo apt-get install mongodb-org-server” References https://github.com/travis-ci/travis-ci/issues/2246 https://github.com/kraih/mango/blob/master/.travis.yml

July 11, 2014

Configuring TravisCI and Coveralls for a node.js project

First include your coveralls token in. .coveralls.yml service_name: travis-pro repo_token: your_token We were already using mocha. And to get the coverage data I’ve added istanbul plus the coveralls module for actually submitting that data to coveralls. package.json "devDependencies": { "mocha": "~1.19.0", "istanbul": "^0.2.10", "coveralls": "^2.10.0" } Makefile In the Makefile, I’ve added a build step called test-coveralls test-coveralls: @NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha */test/*.js --report lcovonly -- -R spec -t 30000 Which, when run will generate coverage data and save it in ...

May 30, 2014

Solving 'Rails bug': 3.0.20: UTF8-encoded JSON request params result in Encoding::CompatibilityError

So I ran acrross a bug: 3.0.20: UTF8-encoded JSON request params result in Encoding::CompatibilityError (https://github.com/rails/rails/issues/9136( in Rails 3.0.20. Which is no longer supported. And ended up writing the Rack middlewear listed below as a ‘fix’. My code is heavily inspired by this post here by Pivotal Labs: Sanitizing POST params with custom Rack middleware (http://pivotallabs.com/sanitizing-post-params-with-custom-rack-middleware/) Rack Middlewear for force_encoding request bodies to UTF-8 JSONScrubber # config/initializers/json_scrubber.rb require 'uri' class JSONScrubber def initialize(app, options) @app = app @routes = options[:routes] end def call(env) scrub(env) @app.call(env) end private def scrub(env) return unless @routes.include?(env['PATH_INFO']) rack_input = env['rack.input'].read.force_encoding(Encoding::UTF_8) env['rack.input'] = StringIO.new(rack_input) ensure env['rack.input'].rewind end end Rails.application.config.middleware.insert_before(0, JSONScrubber, routes: ['/api/some_action_here.json']) JSONScrubberSpec # json_scrubber_spec.rb # encoding: UTF-8 require 'spec_helper' describe JSONScrubber do let(:app) { ->(env) { [200, env, "app"] } } let :middleware do JSONScrubber.new(app, routes: ['/api/some_action_here.json']) end it 'converts non UTF-8 (ASCII-8BIT) encoded response bodies (with special characters) to UTF-8 encoded response bodies' do env = env_for('http://app.abcapp.com.au/api/some_action_here.json') request_array = set_input_and_call env, 'ASCII-8BIT' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('UTF-8') end it 'process UTF-8 encoded response bodies (with special characters) without issue' do env = env_for('http://app.abcapp.com.au/api/some_action_here.json') request_array = set_input_and_call env, 'UTF-8' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('UTF-8') end it "will only modify requests who's paths match the given path options" do env = env_for('http://app.abcapp.com.au/api/dont_touch_me.json') request_array = set_input_and_call env, 'ASCII-8BIT' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('ASCII-8BIT') end def env_for url, opts={} Rack::MockRequest.env_for(url, opts) end def set_input_and_call env, encoding rack_input = %q("{"data":{"name":"Name ❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € → ☎ ❄ ♫ ✂ ▷ ✇ ♎ ⇧ ☮ ♻ ⌘ ⌛ ☘","email":"[email protected]","org":"Sch☂ ☻ool","day":"Teacher"}}") env['rack.input'] = StringIO.new(rack_input.force_encoding(encoding)) env['rack.input'].rewind middleware.call(env) end end This should force_encode all request bodies to UTF-8. So if they happen to have any special unicode characters it won’t break your code. ...

May 16, 2014