Question

In a Ruby project I'm working on, I add ActiveRecord-style, MVC functionality to model classes with a mixin architecture similar to the following:

module Model

  # Classes that mixin this module gain ActiveRecord-style class methods
  # like Model.all, Model.first, Model.last et al.
  #
  # Throughout this module, @@database_bridge will contain a reference to a
  # database ORM bridge, that does the dirty implementation of these methods.

  def all
    # Implementation stuff here, using @@database_bridge as appropriate
  end

  def first
    ...
  end

  # et al

end


class ExampleModel

  extend Model

  # Model-specific implementation goes here...

end

Calling e = ExampleModel.first would assign the first ExampleModel in the database to e.

I want to use dependency injection to set @@database_bridge at runtime, such that every class containing extend Model uses the same, specified ORM object.

How can I do this?

If I could write some kind of helper method to set up that class variable on demand, that would be great.

Was it helpful?

Solution 2

I've found a better solution than the previous, and it's much simpler than I thought (d'oh!): by prefixing a method with self. in a mixin module, a public interface to that method via Module.method becomes available.

Thus, we simply add a setter to our module, using a self.attribute_set statement.

In the above example, the above approach would yield the following code:

module Model

  # Classes that mixin this module gain ActiveRecord-style class methods
  # like Model.all, Model.first, Model.last et al.
  #
  # Throughout this module, @@database_bridge will contain a reference to a
  # database ORM bridge, that does the dirty implementation of these methods.

  def all
    # Implementation stuff here, using @@database_bridge as appropriate
  end

  def first
    ...
  end

  def self.set_database_bridge(ref_to_new_bridge)
    @@database_bridge = ref_to_new_bridge
    ## any additional intialisation/sanitisation logic goes here
  end

  # et al

end


class ExampleModel

  extend Model

  # Model-specific implementation goes here...

end

Calling Model.set_database_bridge would allow us to pass in a new database bridge.

If we don't actually need any initialisation or sanitisation logic in our helper function, there is another, more elegant approach - add an attr_accessor within a class << self block, thus:

module Model

  # ...

  class << self
    attr_accessor :database_bridge
  end

end

This way, we can call Ruby's standard setter method: Model.database_bridge = the_new_bridge.

Sweet.

OTHER TIPS

This isn't the answer, but is a potential solution: you can call class_variable_set on a module, by calling Module.class_variable_set.

Thus, you could create a helper method, in an appropriate namespace somewhere, that calls Module.class_variable_set :@@class_var, "new value".

For the example above, my helper function would like this:

def set_database_bridge(new_bridge)
  Model.class_variable_set :@@database_bridge, new_bridge
end

This solution creates a degree of coupling between the helper function and the implementation of the Model mixin, as if the name of @@database_bridge were to change, the helper function would break.

If anyone has an idea for a more loosely coupled/more encapsulated solution (where we perhaps encapsulate the helper function within Model somewhere), that would be awesome!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top