Dynamically Adding Nested Resource Routes in Rails

I’m working what feels like a rather large project using the Neo4j.rb gem (which recently had its 3.0 release!). One feature of this project allows users to share different types of events with other users. Access to an endpoint in the API is based on whether a given user has a relationship to the target and, if so, some properties of that relationship. So, for instance, a User who has a direct relationship to an Event with the right score may see some restricted properties; a User who is related to an object that is related to that Event will see some limited properties; a User with no relationship whatsoever will not even be able to get to the page.

One of the nice things about the way this is setup is that all of the relationships share properties and some behavior, so it’s begging to be abstracted out into a module that I can test once and share with my resources. It also means that if I’m not careful, I’ll have to duplicate a lot of basic setup code: routes, controllers, etc,… I want to do this:

class Api::V1::EventsController & ApplicationController
  has_users_route

  # normal methods
end

…and have it automatically add a `:users` resource under `event`, so I can do `/api/v1/events/:event_id/users/:user_id` and it will route to the `UserSecurity` controller. This will prevent me from having to do this:

namespace :api do
  namespace :v1 do
    resources :events do
      resources :users, to: 'user_security'
    end
    resources :bands do
      resources :users, to: 'user_security'
    end
    # repeat about 15 times
  end
end

The question, then, is… how the hell do I do this? I did some research and found a lot of information on using Engines to add routes to apps, but this didn’t feel quite right. Someone on StackOverflow pointed me this. It was different from what I had in mind in that it was registering the new resources directly from `routes.rb`. At first, I didn’t think it was going to fit. I found a way to make it use a method in my controller, celebrated, began writing tests and this post… and realized that it was wiping out all the existing routes and just adding the new ones! Womp womp.

So I abandoned the idea of calling methods from controllers and I’m sticking it in `routes.rb`. This is smarter because (as we already covered) this really is a routing issue. By calling a method from the routes file, I can very easily manage which resources are taking advantage of this feature.

With all that said, my `UserAuthorization` module ended up looking like this:

module UserAuthorization
  extend ActiveSupport::Concern

  module ClassMethods
    def register_new_resource(controller_name)
      MyApp::Application.routes.draw do
        puts "Adding #{controller_name}"
        namespace :api do
          namespace :v1 do
            resources controller_name.to_sym do
              resources :users, controller: 'user_security', param: :given_id
            end
          end
        end
      end
    end
  end
end

`routes.rb` looks like this:

['events', 'bands', 'and so on'].each { |resource| ApplicationController.register_new_resource(resource) }

Calling `Rails.application.routes.named_routes.helpers` from the Rails console showed that my new resources were added. Victory! My request specs also suddenly changed and showed that my endpoints had come to life. There was a new problem, though, in the form of `params`.

It’s like this: since UserSecurityController is receiving data from any number of endpoints, I have a mystery resource and mystery param ID: `/api/v1/mystery_resource/:mystery_id/users/:id`. My controller actions need an easy way to get access to each of those and find the appropriate models the user is trying to load.

I started by trying to use the `param` option in the routes like this:

resources controller_name.to_sym, param: :target_id do
  resources :users, controller: 'user_security', param: :given_id
end

All that did was given me `param[:mystery_resource_target_id]` and `param[:given_id]`. The mystery resource — the target the user is trying to modify — was still unidentified, it just had `target_id` appended to it. Some more searches indicated that it might not be possible to change this, so I went in the other direction: If I can’t change the param’s key, I can figure out the path taken that ended up at the controller and set the param accordingly. While I was at it, I added a method to help me find the model that is responsible for the target so I can do things like `target_model.where(whatever)`.

Here’s the resultant class.

class Api::V1::UserSecurityController & ApplicationController
  before_action :authenticate_user!
  before_action :target_id

  private
  attr_reader :root_resource

  def target_id
    @target_id ||= get_target_id
  end

  def get_target_id
    @root_resource = request.fullpath.split('/')[3].singularize
    params["#{root_resource}_id".to_sym]
  end

  def target_model
    @target_model ||= root_resource.capitalize.constantize
  end

  def given_id
    params[:given_id]
  end
end

I don’t love how I had to do that but it gets the job done and I can and will always refactor. There’s still a lot to do but this is a start. Hope it gives you some ideas.

How I Refactor

You can find a gist of this at https://gist.github.com/subvertallchris/1c6397ea7d66be0c0aab.

/u/zaclacgit on Reddit posted a topic the other day asking for thoughts on his exercise of recreating some basic enumerable methods. I gave him some tips on refactoring one method in particular and a few days later, he asked me to elaborate. I thought the easiest way might be to go through each step of the refactor and I’d get a nice blog post out of it in the process.

To use this, start by commenting out each my_inject definition except the first. As you encounter new ones, uncomment them. Save this file as refactor.rb in the folder of your choice, ensure you have the rspec gem installed and run rspec refactor.rb from CLI to execute.

When you can look at your code and see repeating patterns, work to reduce all the unique parts of the repeating code. Reduce everything to variables before you enter your if statements. Never repeat yourself if you can avoid it.

Before we write a line of code, we’re going to write specs based off of the simple comparisons you were doing in your version. It makes it much clearer when there’s a problem. I’m going to show it as a comment here but it’s really going to live at the bottom of my file. You can redfine the same method over and over again, Ruby will execute the last one it finds.

public :my_inject, :my_each
require 'rspec'

describe 'inject with' do
  let(:a) { [1, 2, 3, 4] }

  context 'sym' do
    subject { a.my_inject(:+) }
    it { is_expected.to eq a.inject(:+) }
  end

  context 'int and sym' do 
    subject { a.my_inject(100, :+) }
    it { is_expected.to eq a.inject(100, :+) }
  end

  context 'block' do
    subject { a.my_inject { |memo, x| memo + x } }
    it { is_expected.to eq a.inject { |memo, x| memo + x } }
  end

  context 'int and block' do
    subject { a.my_inject(100) { |memo, x| memo + x } }
    it { is_expected.to eq a.inject(100) { |memo, x| memo + x } }
  end
end

It’s important that we run the spec as we refactor. Pretty code is nice, working code is better, and working code that’s pretty is best. You should code in that order: make it work first, then worry about what you can do to make it more efficient and easier to read.

Since all the methods rely on your my_each method, we’ll include that here at the top.

def my_each
  n = self.length
  i = 0
  while i & n
    yield(self[i])
    i += 1
  end
  self
end

We start here.

def my_inject(initial = nil, sym = nil)
  case 
  when initial.is_a?(Symbol)
    sym = initial
    memo = self[0]
    self[1..-1].my_each do |x|
      memo = memo.send(sym, x)
    end
    memo
  when initial && sym
    memo = initial
    self.my_each do |x|
      memo = memo.send(sym,x)
    end
    memo
  when initial && block_given?
    memo = initial
    self.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  when block_given?
    memo = self[0]
    self[1..-1].my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

Each of the when statements is very similar. There are subtle differences but we don’t want to focus on that, we want to expose patterns. They all kind of look like this:

when some_condition
  memo = something
  something_or_other.my_each do |x|
    memo = some_combination_of_memo(var1, var2)
  end
  memo
end

To me, the most obvious place to start is with the starting memo. In two cases, it’s equal to initial, in the other two it’s self. Rather than defining that each time, let’s define it before we enter the when clauses.

def my_inject(initial = nil, sym = nil)
  memo =  if initial.is_a?(Symbol) || (!initial && block_given?)
            self[0]
          else
            initial
          end
  case 
  when initial.is_a?(Symbol)
    sym = initial
    self[1..-1].my_each do |x|
      memo = memo.send(sym, x)
    end
    memo
  when initial && sym
    self.my_each do |x|
      memo = memo.send(sym,x)
    end
    memo
  when initial && block_given?
    self.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  when block_given?
    self[1..-1].my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

That’s cool. Each when clause has a call to my_each after either self or self[1..-1]. Why is it different each time? Set it once at the beginning.

def my_inject(initial = nil, sym = nil)
  memo =  if initial.is_a?(Symbol) || (!initial && block_given?)
            self[0]
          else
            initial
          end
  starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                self[1..-1]
              else
                self
              end
  case 
  when initial.is_a?(Symbol)
    sym = initial
    starting.my_each do |x|
      memo = memo.send(sym, x)
    end
    memo
  when initial && sym
    starting.my_each do |x|
      memo = memo.send(sym,x)
    end
    memo
  when initial && block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  when block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

We’re getting better but what do we have now? We have duplicate code right at the start! That can be fixed easily. We don’t want to set the memo and starting variables within the if statement, so let’s do it using the output of if. We can set multiple variables to elements of an array.

def my_inject(initial = nil, sym = nil)
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  case 
  when initial.is_a?(Symbol)
    sym = initial
    starting.my_each do |x|
      memo = memo.send(sym, x)
    end
    memo
  when initial && sym
    starting.my_each do |x|
      memo = memo.send(sym,x)
    end
    memo
  when initial && block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  when block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

Getting there. What pops up now? Hopefully that the last two when clauses are identical aside from their conditions. The conditions used to matter when we were setting variables within them. Since that’s not happening anymore, we can clean that up.

def my_inject(initial = nil, sym = nil)
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  case 
  when initial.is_a?(Symbol)
    sym = initial
    starting.my_each do |x|
      memo = memo.send(sym, x)
    end
    memo
  when initial && sym
    starting.my_each do |x|
      memo = memo.send(sym,x)
    end
    memo
  when block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

What about that whole initial/sym thing? If not for that, the first two when clauses would be identical, so let’s declare sym before when and then we won’t need both of those.

def my_inject(initial = nil, sym = nil)
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  symbol = sym || initial
  case
  when initial
    starting.my_each do |x|
      memo = memo.send(symbol, x)
    end
    memo
  when block_given?
    starting.my_each do |x|
      memo = yield(memo,x)
    end
    memo
  end
end

This seems to make sense but we run our specs and… a failure!

1) inject with int and block 
     Failure/Error: memo = memo.send(symbol, x)
     TypeError:
       100 is not a symbol
     # ./refactor.rb:249:in `block in my_inject'
     # ./refactor.rb:5:in `my_each'
     # ./refactor.rb:248:in `my_inject'
     # ./refactor.rb:309:in `block (3 levels) in &top (required)>'
     # ./refactor.rb:310:in `block (3 levels) in &top (required)>'

What gives? Well, since our starting value can be a symbol or an integer, we need to approach that line we just added a bit differently. We have been looking for the presence of initial but maybe that’s not the best way of handling it. Since rspec is complaining about what we are feeding send, let’s focus on that. We need to determine if there is a symbol to send and, if so, what is it? Then we only want to send if there’s a symbol.

Along the way, we’re going to fix that whole case/when situation. Both of the remaining cases start and end the same way, so let’s wrap that around the conditions and then perform some logic inside.

def my_inject(initial = nil, sym = nil)
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  symbol = sym ? sym : (initial.is_a?(Symbol) ? initial : nil)
  starting.my_each do |x|
    memo = memo.send(symbol, x) if symbol
    memo = yield(memo, x) if block_given?
  end
  memo
end

We are aaaaalmost done. This new code is better but this line is kind of silly:


symbol = sym ? sym : (initial.is_a?(Symbol) ? initial : nil)

It’s certainly better than this:

if sym
  sym
else
  if initial.is_a?(Symbol)
    initial
  else
    nil
  end
end
But we can just use the   operator to handle some of logic.
def my_inject(initial = nil, sym = nil)
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  symbol = sym || (initial.is_a?(Symbol) ? initial : nil)
  starting.my_each do |x|
    memo = memo.send(symbol, x) if symbol
    memo = yield(memo, x) if block_given?
  end
  memo
end

It’s still an important line to understand because it makes our line #317 possible

symbol = sym || (initial.is_a?(Symbol) ? initial : nil)

The key is that we’re declaring symbol, just like we declare initial and sym as defaulting to nil in the method. We need symbol to exist or this line will fail:

memo = memo.send(symbol, x) if symbol

We use the parenthesis to control the folow of logic, with the ternary operator reducing this:

if initial.is_a?(Symbol)
  initial
else
  nil
end

To a simple one-line expression.

expression_returning_boolean ? do_this_if_true : do_this_if_false

I was testing it and realized that we’ve left something out, haven’t we? We’re testing sym, int and sym, block, int and block, but what if someone gives int and block and sym? They shouldn’t do that, so let’s look for that right at the beginning.

def my_inject(initial = nil, sym = nil)
  raise 'Cannot pass int, sym, and block' if initial && sym && block_given?
  memo, starting =  if initial.is_a?(Symbol) || (!initial && block_given?)
                      [self[0], self[1..-1]]
                    else
                      [initial, self]
                    end
  symbol = sym || (initial.is_a?(Symbol) ? initial : nil)
  starting.my_each do |x|
    memo = memo.send(symbol, x) if symbol
    memo = yield(memo, x) if block_given?
  end
  memo
end

And we’ll modify our tests to check for that, too. Uncomment the tests below, comment out the tests at the bottom of the page to execute.

public :my_inject, :my_each
require 'rspec'

describe 'inject with' do
  let(:a) { [1, 2, 3, 4] }

  context 'sym' do
    subject { a.my_inject(:+) }
    it { is_expected.to eq a.inject(:+) }
  end

  context 'int and sym' do 
    subject { a.my_inject(100, :+) }
    it { is_expected.to eq a.inject(100, :+) }
  end

  context 'block' do
    subject { a.my_inject { |memo, x| memo + x } }
    it { is_expected.to eq a.inject { |memo, x| memo + x } }
  end

  context 'int and block' do
    subject { a.my_inject(100) { |memo, x| memo + x } }
    it { is_expected.to eq a.inject(100) { |memo, x| memo + x } }
  end

  context 'int, block, and sym' do
    it 'raises an error' do
      expect { a.my_inject(100, :+) { |memo, x| memo + x } }.to raise_error
    end
  end
end

And there you have it! From 27 lines down to 12. It could always be a bit more concise but for me, it’s perfectly readable and won’t require a ton of head-scratching if I ever have to revisit it.

Hope this helps. Get in touch if you have any questions, [email protected].

public :my_inject, :my_each
require 'rspec'

describe 'inject with' do
  let(:a) { [1, 2, 3, 4] }

  context 'sym' do
    subject { a.my_inject(:+) }
    it { is_expected.to eq a.inject(:+) }
  end

  context 'int and sym' do 
    subject { a.my_inject(100, :+) }
    it { is_expected.to eq a.inject(100, :+) }
  end

  context 'block' do
    subject { a.my_inject { |memo, x| memo + x } }
    it { is_expected.to eq a.inject { |memo, x| memo + x } }
  end

  context 'int and block' do
    subject { a.my_inject(100) { |memo, x| memo + x } }
    it { is_expected.to eq a.inject(100) { |memo, x| memo + x } }
  end
end

Relationships and Rails OOP: The Missing Link

In a vanilla Rails app, an association is really the byproduct of two objects referring to each other. Object 1 has an ID that gets referenced in a join table that also references Object 2’s ID, or maybe Object 1 has a column in its table that references Object 2’s ID — it doesn’t really matter how they refer to each other, cause the point is that the association doesn’t really exist the way the rows describing Objects 1 or 2. Since the association is just that, a literal association of one object to another, and not an object, it doesn’t really need much management, per se, since two objects are either related or they’re not.

In Neo4j, the idea that relationships are objects and are therefore just as real as nodes is the central part of its philosophy. It’s what separates a graph database from all other databases: your relationships are part of the data, your relationships are objects.

I’ve been using Neo4j.rb, an ActiveRecord replacement that uses Neo4j, in Phillymetal and other projects for about a year and a half now and started contributing to the development of v3.0 a little while ago. An issue I’ve always had with the shoehorning of Neo4j into Rails is that Rails isn’t really equipped to handle relationships as objects. I mean, it’s easy to picture how an ActiveRecord model and a Neo4j model parallel one another: what was once a row in the database now becomes a node. Associations in ActiveRecord make sense in principle — your association is a relationship and where once lived an ID referencing another row, you now have a relationship — but this doesn’t really go far enough, does it? In ActiveRecord, an association is a byproduct; in Neo4j, a relationship is truly an object.

This leads us to a philosophical issue when we approach it from an object-oriented perspective. Say I have two models, Student and Lesson. A student has many lessons, a lesson has many students. Which model is responsible for the relationship between student and lesson? In ActiveRecord, it wouldn’t be much of an issue since the association isn’t something that really needs responsibility. There are no properties, there are no methods, there are no instances; each object is essentially responsible for itself and the association is something that happens. But if we are adding properties to a relationship in Neo4j, if we are to assume that it truly is an object, should

  • either model be responsible for that relationship? I’d argue no.

Here’s an example of where it goes wrong:

class User
  include Neo4j::ActiveNode
  has_many :out, :managed_lessons, model_class: Lesson, type: 'manages'

  def create_lesson_rel(lesson)
    if lesson.respond_to?(:managed_by) 
      # create relationship
      # add properties to the relationship
      # call methods on lesson to modify the node
    else
      # failure behavior
    end
  end
end

Why is User responsible for doing all of that? Because either it or the lesson object has to, even though Neo4j makes it clear that the relationship is part of our data and is its own model. In response, we need a new type of model, we need a relationship model. It is literally the missing link between nodes.

In Neo4j.rb 3.0, the ActiveRecord replacement for nodes is called ActiveNode. A few days ago, we released v3.0.0.alpha.10, containing ActiveRel, the relationship wrapper. It solves this problem by offering the ability to create relationship models that behave exactly as you would expect, complete with instances that support validations, callbacks, declared properties, and so on. They offer a separation of relationship logic, so models only need to be aware of the associations between nodes.

In practice, a slightly advanced implementation can looks like this:

class User
  include Neo4j::ActiveNode
  property :managed_stats, type: Integer #store the number of managed objects to improve performance

  has_many :out, :managed_lessons,  model_class: Lesson,  rel_class: ManagedRel
  has_many :out, :managed_teachers, model_class: Teacher, rel_class: ManagedRel
  has_many :out, :managed_events,   model_class: Event,   rel_class: ManagedRel
  has_many :out, :managed_objects,  model_class: false,   rel_class: ManagedRel

  def update_stats
    managed_stats += 1
    save
  end
end

class ManagedRel
  include Neo4j::ActiveRel
  after_create :update_user_stats
  before_create :set_performance_review
  validate :manageable_object
  from_class User
  to_class :any
  type 'manages'

  property :created_at
  property :updated_at
  property :next_performance_review, type: DateTime

  def update_user_stats
    from_node.update_stats
  end

  def set_performance_review
    next_performance_review = 6.months.from_now
  end

  def manageable_object
    errors.add(:to_node) unless to_node.respond_to?(:managed_by)
  end
end

# elsewhere
rel = ManagedRel.new(from_node: user, to_node: any_node)
if rel.save
  # validation passed, to_node is a manageable object
else
  # something is wrong
end

As you can see, our User model is aware of associations to three different classes plus one, `managed_objects` that does not specify a class and will return all nodes related by type `manages`. Since they all refer back to the ManagedRel class, they all draw from the same `type`. As long as we create the relationship using an instance of ManagedRel, our validations and callbacks will run. The relationship sets its own timestamps and uses a callback to set its own property, then calls the method on the user to update its stats. We have separated the bulk of our relationship logic and can sleep soundly.

RSpec Mocks for the Sad and Desperate

No matter how good I think I am at something, I try to remember that I’m always learning and I can always be better. This is especially true with all things technological, whether it’s server or code, and one of my favorite things to suck less at is RSpec. When I first started working with Ruby and Rails, I was mystified by it. Written poorly, a spec quickly turns into a mess of muddled expectations and shoulds, vague statements in large blocks that can require more management than the code it claims to prove; written well, it’s a dream, a revolution, a safety net and documentation all in one.

Lately, I’ve been working on leveling up my specs quite a bit. It’s sort of embarrassing to say but I had a really hard time understanding exactly how or why someone would use mocks within a spec. I mean, I understood the concept — stand-ins for objects and methods to ensure you are only testing a small subset of your code, not related objects or methods — but I found the example code somewhat hard to read. It wasn’t until I committed a little time and had to start writing my own that I realized it isn’t difficult at all, it’s just that most of the resources out there that attempt to explain them do a crappy job.

So here’s the deal: a double is a dummy object that acts as a stand-in for a real object. I’d say to think of it like a stunt double but it’s more of a crash test dummy, a mannequin object without methods of its own. It’s a shell, a placeholder, and you use stubs to create its methods and responses.

Let’s pretend we want to test a method, `get_score`. This method exists in a mixin and performs some sort of magic on User objects to return the user’s score. The thing is, since your module doesn’t actually retrieve the score, you don’t want to test anything on User, you just want to make sure that the proper call is made. To do that, you create a double to stand in for a user.

describe MyModule::ScoreGetter do

  let(:clazz) do
    Class.new do
      include MyModule::ScoreGetter #contains the get_score method
    end
  end


  describe 'method get_score' do
    let(:user) { double("a user object") }
    let(:obj) { clazz.new }

    it 'returns a score' do
      expect(obj.get_score(user)).to eq 50
    end
  end
end

Note that we created a double to represent a user instead of calling User.new. Easy so far, but now what? Now we start stubbing some methods.

RSpec’s documentation says a stub is “an instruction to an object (real or test double) to return a

known value in response to a message.” In practice, that really just means that a stub is a stand-in for calling any actual method. You write the expected response of a method call rather than actually calling the method. WHY, though? Imagine our code for `get_score` looks something like this:

module MyModule
  module ScoreGetter

    def get_score(user)
      complicated_private_method(user)
    end

    private

    def complicated_private_method(user)
      #all sorts of wacky shit, it's preparing stuff on your calling object or something, ultimately ending in...
      user.score_generation(self)
    end
  end
end

class User
  #code

  def score_generation(caller)
    #sophisticated score generation algorithm
  end
end

We stub because we don’t want to test `score_generation`. It goes outside the scope of this unit test, maybe it involves a database and we don’t want to perform queries, maybe it’s slow, maybe it uses someone else’s code and it’s unstable. If it fails, your `get_score` method will fail, even if `get_score` isn’t broken! All we want to test is:

  • our class calls `get_score`
  • `complicated_private_method` calls `user.score_generation` and returns a score

That’s it. Doing this is easy.

  it 'returns a score' do
    expect(user).to receive(:score_generation).with(obj).and_return(50)
    expect(obj.get_score(user)).to eq 50
  end

You are expecting `user` to receive the method `score_generation` with `obj` as a parameter and you decided it will return `50`. The syntax here might be a little misleading: you aren’t expect it to return 50, you are saying IT WILL RETURN 50. Your expectation is on the method and its parameter.

Alternatively, if you don’t want to declare at as an expectation and just want a single expectation for your test, you can use the `stub` method with a very similar syntax.

  it 'returns a score' do
    user.stub(:score_generation).and_return(50)
    expect(obj.get_score(user)).to eq 50
  end

All we’re saying is, “when the user double receives a call to `score_generation`, return 50.” This is not an expectation, though, so your spec won’t fail if this doesn’t happen unless another expectation relies on it. In other words, we could have this:

  it 'returns a score' do
    user.stub(:score_generation).and_return(50)
    user.stub(:do_this_thing).and_return(:foo)
    expect(obj.get_score(user)).to eq 50
  end

…and your spec would still pass because `do_this_thing` is acting as a double’s method definition, not an expectation. Change that to `expect` and your spec’s passing depends on it.

Again, we have to do this because our double doesn’t have a `score_generation` method and even if it did, we wouldn’t want it to actually be called because we don’t want to rely on that class or its damned dirty methods. An important thing to remember is to declare your stubs before the method is actually called; if you don’t, the method will be called before the stub is in place and it won’t know how to behave.

For me, one of the trickier parts of working with this stuff comes when I’m looking at someone else’s code and they’re using a lot of stubs to unfamiliar methods. Effective stubbing expects you to understand the flow of messages within the code. There have been occasions where I’ll add or modify a method and not have the test show the expected change, only to discover that a method further up the chain has been stubbed and my method isn’t actually being called! Read carefully, understand completely.

That’s it for now. Hopefully this will help someone along!

Neo4j Lessons Learned Followup

It’s been just over two months since my last post, where I wrote about building and self-hosting a site on a budget with Rails and Neo4j. Since then, I haven’t made many changes to the site itself — a couple styling tweaks, some new admin controls — but I’m happy to report that it’s been rock solid, extremely fast, and yielded unanimously positive feedback. Go team! There are two updates to that article that I want to address.

First, while researching the optimization of Cypher queries, I came across a mention of a simple way to improve performance when using the embedded database. I checked against the code in neo4j-core and found that it wasn’t following this best practice, so I made the change. It’s been committed but since there hasn’t been (and won’t be) a new version of the gem pushed on the v2.x branch, you should update your Gemfile to reflect it.

gem ‘neo4j-core’, git: ‘[email protected]:andreasronge/neo4j-core.git’, ref: ‘99644256a6

I do so much caching in Rails can’t claim I saw a huge change on my server, but my tests in the console confirmed that it worked. It changes the way the query engine works and lets the database cache queries correctly. It certainly can’t help.

Second, I neglected to mention a really important lesson from my experience:

Never trust relationship order to be consistent and predictable.

Phillymetal.com uses a node for shows, additional nodes for bands, with a relationship (“playing_shows” or something) between them. The ORDER of the bands is very important. When I tested, I found that it was consistently returning the bands in the order that they were specified after I imported the data.

Unfortunately, a few days after the site launched, I discovered that on my production server, new show submissions would be in the wrong order immediately after the form was submitted, then they’d flip to the right order after the show was modified in any way. It was odd. After a little while later, I found the problem, but it doesn’t matter because I was wrong to assume it would just be in the right order in the first place.

If you want a series of relationships to appear in the right order, put a property on the relationship that you can use to sort. In my case, I actually had been saving the index from the submitted form just in case, I just wasn’t actually using it to sort, so my fix was easy. Don’t make the same mistake.

If you’re in NYC and want to talk about any of this stuff (or at least listen to me talk about it), I’ll be presenting at the NYC Neo4j Meetup on July 21, 2014. Info here. It’s just going to be a discussion and Q&A. I expect we’ll cover a lot of the same stuff as these blog posts but considering how smart the crowd at the last Meetup was, I can only imagine that I’ll learn a bit from it, too.

That’s it for now. Lots more to write about later, hope someone finds this stuff useful!

 

subscribe via RSS