Week 30

I didn’t realize how fast time flies….

Sinatra

Sinatra explained by Zhengjia: great tutorial, dive deep into how Sinatra works.

Let’s Build a Sinatra – by Gabe Berke-Williams He is showing how to build a Nancy – a similar type of Sinatra – from scratch. I can now understand how to build the Classic type

Almost Sinatra in 6 lines of code

How to enable sessions with Sinatra: overview of the defaul Sinatra session, then Rack::Session::Cookie, and the last Rack::Session::Pool

Delegator

To provide syntatically sugar for Sinatra Classic style

TIL

git clean

The documentation for git clean is clear, but still it’s long. So here comes the summary

git clean -n # Dry run, show list of untracked files to be deleted
git clean -n -x # Dry run, show list of untracked + ignored files to be deleted

git clean -f # Remove untracked files

git clean -x -f # Remove untracked + ignored files

The order of middlewares matter

Despite having working with Ruby for more than 6 months, I have never really understood the usage of middlewares inside a Rack application.

We are working on a problem to set the X-Request-Id header for the incoming request, and we would like to have that header in the response as well.

So for setting the header, we use a middleware layer, named RequestId. Initially I thought we would need another middleware to set a header for a response as well, but no, we do not need it.

A request is coming to Rack app with its set of headers. Then whenever we modify the headers for the Rack app

This is the sample response from Rack app. It’s an array with 3 elements:

  • an Integer: HTTP status code
  • a Hash: Some headers
  • an Array: The response body
map <span class="hljs-string">'/posts'</span> <span class="hljs-keyword">do</span>
run Proc.<span class="hljs-keyword">new</span> { |env| [<span class="hljs-string">'200'</span>, {<span class="hljs-string">'Content-Type'</span> => <span class="hljs-string">'text/html'</span>}, [<span class="hljs-string">'first_post'</span>, <span class="hljs-string">'second_post'</span>, <span class="hljs-string">'third_post'</span>]] }
end

Each Rack app is required to return the array with 3 elements. Hence once we have a middleware to modify the response of Rack app, then it’s there.

To read more: Proper Rack Middleware Ordering | Verbose Logging – this is really useful website that help me to understand how the chain of middlewares are interacting with each other.

String interpolation in Ruby

greek_to_roman_gods =
{
zeus: 'Jupiter',
hera: 'Juno',
poseidon: 'Neptune',
hades: 'Pluto',
aphrodite: 'Venus',
ares: 'Mars',
artemis: 'Diana',
athena: 'Minerva',
hermes: 'Mercury',
demeter: 'Ceres'
}

"A heavenly marriage: #{greek_to_roman_gods[:zeus]} and #{greek_to_roman_gods[:hera]}"

format('A heavenly marriage: %s and %s', greek_to_roman_gods[:zeus], greek_to_roman_gods[:hera])
format('A heavenly marriage: %{zeus} and %{hera}', greek_to_roman_gods)

'A heavenly marriage: %s and %s' % [greek_to_roman_gods[:zeus], greek_to_roman_gods[:hera]]
'A heavenly marriage: %{zeus} and %{hera}' % greek_to_roman_gods

# All of them return the same result
=> "A heavenly marriage: Jupiter and Juno"

If you save that file, and run rubocop on that file. You will get the offense Favor format over String#% for line 18-19. Look further into the rubocop gem, it’s coming from  RuboCop::Cop::Style::FormatString

This cop enforces the use of a single string formatting utility. Valid options include Kernel#format, Kernel#sprintf and String#%.

The detection of String#% cannot be implemented in a reliable manner for all cases, so only two scenarios are considered - if the first argument is a string literal and if the second argument is an array literal.

I listed 5 ways to do string interpolation. Let’s go through each of them:

  1. String interpolation in double-quoted string
  2. format with additional arguments
  3. format with a hash argument
  4. String#% with a hash
  5. String#% with an array

The first, and the common one, using the hash symbol in double-quoted string.

"A heavenly marriage: #{greek_to_roman_gods[:zeus]} and #{greek_to_roman_gods[:hera]}"

Then we can go ahead and use sprintf, or there is an alias for that in ruby: format. You can supply a number of variables to replace the %s

format('A heavenly marriage: %s and %s', greek_to_roman_gods[:zeus], greek_to_roman_gods[:hera])

Or you can supply a Hash, and format replace the value of matching hash key:

format('A heavenly marriage: %{zeus} and %{hera}', greek_to_roman_gods)

The last two lines use String#%. Here is the doc:

str % arg → new_str
Format—Uses str as a format specification, and returns the result of applying it to arg. If the format specification contains more than one substitution, then arg must be an Array or Hash containing the values to be substituted. See Kernel::sprintf for details of the format string.

rubocop doesn’t approve using String#% for some stated reason above, then we can change safely to use format instead.

Prototype

I’m reading a paper called [Metaprogramming in Ruby – A Pattern Catalog] by Sebastian Gunther and Marco Fischer. They gave some code example, that I did not understand properly in the beginning. Hence, this post aims to clarify the mystery of those lines.

This part of code is from Figure 11, the implementation of Prototype module and its usage inside Hydra class. I modified the code to add some output to see how the code is executed.

module Prototype
  def self.included ( base )
    puts "#{self} included in #{base}"

    base.class_eval do
      puts "#{self}.class_eval is called"

      def initialize
        puts 'Prototype Initialize Called'
        raise " InstantiationError "
      end
    end

    base.instance_eval do
      puts "#{self}.instance_eval is called"

      def create
        puts "create() is called, self = #{self}"
        prototype = self.clone

        prototype.instance_eval do
          puts "prototype.instace_eval() is called"

          def initialize
            puts 'prototype.instance_eval.initialize() is called'
            raise " InstantiationError "
          end
        end

        prototype.instance_eval { def is_prototype?; true ; end }

        puts "... returning a cloned prototype #{prototype}"
        return prototype
      end
    end
  end
end

When we include a module Prototype in a class, we can see the execution. The function included is called when Prototype is included in Hydra. Inside that function, the class_eval and instance_eval do their jobs. `

class Hydra
  include Prototype
end

===> We get
Prototype included in Hydra
Hydra.class_eval is called
Hydra.instance_eval is called

We cannot initiate an instance from Hydra class dge

hydra = Hydra.create

===> We get
create() is called, self = Hydra
prototype.instace_eval() is called
... returning a cloned prototype #<Class:0x007feadd8429f0>

Looking a bit deeper

irb(main):093:0> hydra.methods false
=> [:initialize, :create, :is_prototype?]
irb(main):094:0> hydra.instance_methods false
=> []
irb(main):107:0> hydra
=> #<Class:0x007feadd8429f0>
irb(main):109:0> hydra.included_modules
=> [Prototype, Kernel]

And it’s getting weird here. So Hydra.create is returning cloned object of Hydra instead of an instance of Hydra. And that cloned object, in return, also have a class method create.

irb(main):115:0> Hydra.create.create.create
create() is called, self = Hydra
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadc8240a0>
create() is called, self = #<Class:0x007feadc8240a0>
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadd8fbc70>
create() is called, self = #<Class:0x007feadd8fbc70>
prototype.instace_eval() is called
... returning a prototype #<Class:0x007feadd8fb888>
=> #<Class:0x007feadd8fb888>

Grape Error Formatter

I was working with Grape API, and I use a custom error formatter. Since we are working on generating error objects according to JSON API specification format:

Here is an official example [source]

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/vnd.api+json

{
  "errors": [
    {
      "status": "422",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters."
    }
  ]
}

d But we also want to add

So even though the field jsonapi is not required, we would like to have it in our response. So our response would be like this:

{
"jsonapi": {
"version": "1.0"
},
  "errors": [
    {
      "status": "422",
      "source": { "pointer": "/data/attributes/first-name" },
      "title":  "Invalid Attribute",
      "detail": "First name must contain at least three characters."
    }
  ]
}

 

FactoryGirl create vs. build

Problem: we have the before_save callback for a provider, and we want to test whether this callback is executed correctly.

The test passes

    context 'valid provider' do
      let(:provider) { FactoryGirl.build(:provider) }

      it 'get triggered when saved' do
        expect(provider).to receive(:test_before_save_callback)
        provider.save!
      end
    end

Even when we force the creation of provider with let, the test still passes.

    context 'valid provider' do
      let!(:provider) { FactoryGirl.build(:provider) }

      it 'get triggered when saved' do
        expect(provider).to receive(:test_before_save_callback)
        provider.save!
      end
    end