4

My application configuration includes some values which need to be used in AR relationships. I'm aware this is an odd and potentially criminal thing to attempt, but I need to maintain the configuration as a textfile, and I honestly think I have a good case for a tableless model. Unfortunately I'm having trouble convincing AR (Rails 3.2) not to look for the table. My tableless model:

class Tableless < ActiveRecord::Base

  def self.table_name
      self.name.tableize
  end

  def self.columns
    @columns ||= [];
  end

  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end

  def self.columns_hash
    @columns_hash ||= Hash[columns.map { |column| [column.name, column] }]
  end

  def self.column_names
    @column_names ||= columns.map { |column| column.name }
  end

  def self.column_defaults
    @column_defaults ||= columns.map { |column| [column.name, nil] }.inject({}) { |m, e| m[e[0]] = e[1]; m }
  end

  def self.descends_from_active_record?
    return true
  end

  def persisted?
    return false
  end

  def save( opts = {} )
    options = { :validate => true }.merge(opts)
    options[:validate] ? valid? : true
  end
end

This is extended by the actual model:

class Stuff < Tableless

  has_many :stuff_things
  has_many :things, :through => :stuff_things

  column :id, :integer
  column :name, :string
  column :value, :string

  def initialize(attributes = {})
    attributes.each do |name, value|
      send("#{name}=", value)
    end
  end
end

This is all based on code found here on SO and elsewhere, but alas, I get SQLException: no such table: stuffs: Any clues any one?

Community
  • 1
  • 1
John Schulze
  • 2,198
  • 3
  • 20
  • 22
  • I don't know enough about this to provide an answer, but theres a Railscast on this very subject, albeit a bit old http://railscasts.com/episodes/193-tableless-model – DVG May 24 '12 at 02:51
  • Thanks! Had a look at the episode and it basically outlines the same method that I'm using. It would seem something changed with Rails 3 that affects the ability to spoof AR tables - that or I've missed something else! – John Schulze May 24 '12 at 03:37
  • I know there's a gem called active_attr that also provides some model behavior without a table, might be worth looking into – DVG May 24 '12 at 03:45
  • Thanks again, but that gem doesn't appear to include associations. – John Schulze May 24 '12 at 03:50
  • I have a feeling that you're out of luck. The table-less parts of ActiveRecord are (AFAIK) mostly covered by ActiveModel and doesn't include any of the association stuff. Why can't you put the data into a table? You can still keep the text file and load it into the database as needed. – mu is too short May 24 '12 at 05:45
  • @mu: The data is app instance configuration data; part of it defines a list of what "things" are available in this particular instance. The convenience of being able to do instance.things (things are actual DB objects) instead of having to do things.where("stuff_id = #{STUFF[:id]}") would be nice, but the settings need to remain in a text file for ease of editing, deployment and backup. – John Schulze May 24 '12 at 14:39
  • 1
    Could you load the data into the database in an initializer? There's no reason that you can't have the data in two places. – mu is too short May 24 '12 at 16:27
  • @mu: Yeah, I've now tortured myself with trying to use a hash constant as the data store (manually matching up with my models) but the pain became unbearable. Trying to do a list of checkboxes to toggle some values became an epic four hour battle and I'm forced to admit defeat. A tableless model as per my original plan would have been perfect, and conceptually _very_ close to inserting the data at boot, but if it can't be done then your suggestion is the second best. Thanks for the input! – John Schulze May 25 '12 at 03:22

2 Answers2

3

For Rails >= 3.2 there is the activerecord-tableless gem. Its a gem to create tableless ActiveRecord models, so it has support for validations, associations, types.

When you are using the recommended way (using ActiveModel opposed to ActiveRecord) to do it in Rails 3.x there is no support for association nor types.

Jarl
  • 2,831
  • 4
  • 24
  • 31
  • Funny is i just find it on Github :) I don't much understand the second paragraph :( Do you know if it supports the persisting workflows of ActiveRecord? I am not sure if i'll not cut my rope to the create and update method... – Mailo Světel May 10 '13 at 16:33
  • Depends on what you mean by persiting workflow. The gem discards the persistancy part of ActiveRecord... i.e. it does not persist. – Jarl May 12 '13 at 19:47
1

For Rails >= 4 you can also get support for validations, associations, and some callbacks (like after_initialize) by defining your tableless classes like this:

class Tableless < ActiveRecord::Base
    def self.columns() @columns ||= []; end

    def self.column(name, sql_type = nil, default = nil, null = true)
        columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
    end 

    attr_accessor :id, :name, :value

    has_many :stuff_things
    has_many :things, :through => :stuff_things

end
Eric
  • 3,632
  • 2
  • 33
  • 28
  • Eric, is the column class method strictly necessary? When would it be called? I'm using this in a tabless form model with a few tabless associations, and only new/create actions with calls to valid?. – Dave Feb 02 '16 at 16:41