THE BITESITE BLOG
Bangoperators

Careful with Bang Operators in your Rails Models and ActiveRecord Callbacks (Frozen String error)

coding ruby on rails software

Alright here's a weird one that would have been easy to solve if I thought more about how Ruby works (and how it's not like Java:)).

So here was the issue, we were looping through a set of keys on a hash. Now, you should know that the keys on this hash were strings. So we had something like this

my_hash = {"color" => "blue", "size" => "medium" }

Now, on top of that we had a model that we'll call Person that had a "name" attribute and looked something like this:

class Person < ActiveRecord::Base
  before_validation upcase_name

  private
    def upcase_name
      name.upcase!
    end
end

Ok, for those who don't know, the bang version of upcase, upcase!, uppercases a string in place - it modifies the actual string itself.

So here is the setup, we were looping through keys of the hash:

my_hash.keys.each do |name|
  ...
end

Now, first thing to know is that you CAN'T modify a key in a hash. Anytime you try to modify the keys of a hash, you get an error. So something like this would produce an error:

my_hash.keys.each do |name|
  name.upcase!    # produces frozen string error
end

That all makes sense.

What we were surprised to see was that the following code also produces a similar error:

my_hash.keys.each do |name|
  Person.create(name: name)    # produces frozen string error
end

What's going on here? Well it turns out that the "before_validation" callback tries to upcase the name. But unlike languages like Java, the string that is passed in is the exact same object that exists in the "my_hash.keys" array. (I'm purposely avoiding by-value and by-reference wording here, because I feel it can lead to confusion)

So the hash holds a frozen lock on that string, and then when that string gets passed into the Person constructor, and the callback tries to modify it - you get the frozen string error.

How do you fix it? Change your callback:

class Person < ActiveRecord::Base
  before_validation upcase_name

  private
    def upcase_name
      name = name.upcase
    end
end

This version doesn't modify the original string but rather produces a new string.

Special thanks to d3chapma for helping me figure this one out. Check out his Gist here for more info.

Hope this helps out some peeps!

Caseyli
Casey Li
CEO & Founder, BiteSite
Reactnativeenv

React Native Env 'cannot read property get of undefined'

coding software react native

Alright, in my continuing effort to use React Native, I keep running into little issues with all the libraries I use. Usually it's due to a setup issue and my lack of understanding of the true under-workings of what I'm doing seems to lead to these issues.

Today, it's with react-native-env. When setting this library up, I kept running into an issue that would say 'cannot read property get of undefined' or 'cannot read property getAll of undefined'.

When I started debugging, it seemed the EnvironmentManager object was present, so what was the issue.

If you look at your

node_modules\react-native-env\index.js

you'll see that the get and getAll methods are delegating it to some native code:

RNEnvironmentManagerIOS.get
RNEnvironmentManagerIOS.getAll

So it's really just an issue of not reading the instructions properly. You need to add the RNEnvironmentManagerIOS.h and .m files to your project.

The only thing I found a little misleading is you shouldn't add the folder - rather add the individual files.

So in XCode, (I have a group called 'Libraries' under my project, but I think you can add it anywhere), right-click and "Add files to ", and make sure to select the individual RNEnvironmentManagerIOS.m and RNEnvironmentManagerIOS.h files.

Hope that helps out some peeps!

Caseyli
Casey Li
CEO & Founder, BiteSite
Reactnativevectoricons

React Native Vector Icons getImageForFont issue

coding software react native

So recently, I've been diving into React Native and running into all these little quirks. Luckily, it seems to be getting better day by day in terms of setup. I can say this though, as a Web Developer, this is my most successful attempt and writing mobile apps (after trying Objective-C, then Swift).

Today, I thought I'd post an small issue I ran into in trying to get react-native-vector-icons to work. The error message that would pop up on screen was

getImageForFont issue

It was a pretty simple fix. In Xcode, in your project, find your Info.plist. Then check the "Fonts provided by application" (if it's not there, you'll have to add it). Once added, make sure all your fonts are added to this property. So add Entypo.ttf, FontAwesome.ttf etc.

After that, it should work.

Hope this helps out some peeps.

Caseyli
Casey Li
CEO & Founder, BiteSite
Git icon 1788c

How to automatically execute your RSpec test suite before pushing code

ruby on rails rspec Git Git Hooks TDD FactoryBot Faker Testing Capybara

As you probably know from our previous blog posts, at BiteSite we recently started writing tests for our Rails apps using a combination of RSpec, factory_bot, and faker. We currently only write tests for our controllers and models. However, we are hoping to start writing some unit tests, and UI tests using Capybara.

Even though we love writing tests, we find that we can sometimes forget to run our test suite before merging code into our main branch. This can sometimes lead to failing specs making their way into our develop branch. To try and prevent this from happening, we decided to experiment with a continuous integration service, called Codeship. At first glance, Codeship seem to accomplish exactly what we wanted. However, we quickly reached and surpassed the free tier limit. Thus it was no longer running our test suite on new commits. We would eventually like to host our own CI server; however, we currently work at a shared office where we don't have control over the network setup.

This led me to come up with the idea of using Git hooks. Git hooks are scripts that are executed during a specific Git command. You can find the different types of hooks here https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks. I decided that I was going to use the pre-push hook that was introduced in Git version 1.8.2. Using the pre-push hook, I was able to write a script that ran our RSpec test suite before pushing code to our remote repositories. The Git pre-push hook will push your code if it exits with a zero status a cancel the push if it exits with a non-zero status.

At this point I wasn't sure how I was going to determine whether our test suite passed or not. So I decided to look into whether or not bundle exec rspec returned anything. Fortunately, if all the tests pass, bundle exec rspec returns a 0 if not, it returns a non-zero value. There we go! At this point I had everything that I need to create my pre-push script. Alright enough jibber jabber, let's get to the nitty gritty.

#!/bin/sh
echo "Starting test suite..."

bundle exec rspec

if [ $? == 0 ]; then
  echo ""
  echo "================================================================================"
  echo ""
  echo "Congratulations 🎉 all specs passed!"
  echo ""
  echo "================================================================================"
  echo ""
else
  echo ""
  echo "================================================================================"
  echo ""
  echo "😡 the tests did not pass. No push for you!"
  echo ""
  echo "================================================================================"
  echo ""
  exit 1
fi

exit 0

The script was actually a lot simpler than I thought. We simply run the bundle exec rspec command and check its output status and from that we can allow or prevent the code from being pushed.

If you wanted to try this out for yourself, navigate to your project’s root folder, then into .git/hooks/. Create a file called pre-push with no extension and copy the above script into it. Now we need to make the file executable, to do so, execute the following command from your terminal: chmod +x pre-push.

Now all you need to do is push code to your remote repository and it will run the script. Unfortunately, with the current implementation, you need to wait until the test suite is complete before you can do anything else. Next time we will continue with what we started and turn the script into a background job and use mac notifications to notify us of completion status.

Ryanoconnor
Ryan O'Connor
Software Developer, BiteSite
Rspecrabljsonresponses

Failing RSpec Tests when using RABL for JSON Responses

coding software ruby on rails rspec rabl

After about 5 years of doing Ruby on Rails development, there is one aspect that for the longest time never seemed to sit well with me: TDD. It wasn't that I didn't understand the benefits, and it wasn't that I didn't believe in it, but unlike many things Rails, there wasn't enough concrete motivation to do it. Over the past few months I finally started to get into it and I'll write all about my experiences in another post, but for now, I wanted to let you guys know about a specific problem I was having with TDD'ing an API I built.

I was building an API for a app using Ruby on Rails and following the http://apionrails.icalialabs.com/.

To render my JSON responses, I almost always use RABL now. I tried activemodelserializers when I was experimenting with Ember, but I quickly switched back to RABL.

Anyway, I was writing my tests I was having issues testing my JSON response. My typically test would look something like this:

it "renders a response" do create(:route) get :index, format: :json response_object = JSON.parse(response.body) expect(response_object.size).to eq(1) end

The issue is that no matter what I did, it seemed that my tests would fail. Upon further inspection, it looked like the response.body was empty. I was getting really frustrated so what I did was I abandoned my TDD and just tried it in the browser (well, more accurately in Postman). To my surprise, it was working.

So why were my tests failing and why was the response.body empty?

It all has to do with the fact that I was using RABL for the JSON responses. Because RABL uses Rails view rendering and by default Controller specs don't render views - the response.body was empty.

So the solution was as simple as adding

render_views

to the top of my specs so I had the followiing:

require 'spec_helper' describe Api::V1::MyController, type: :controller do render_views ... end

Alright, that's it for now. Hopefully this helps some peeps.

Caseyli
Casey Li
CEO & Founder, BiteSite