Tableless model in rails 3.1
Asked Answered
T

6

7

Looks like this method doesn't work anymore in rails 3.1. So, does someone have a working solution?

Actually, I've found this gist. It solves problems with columns_hash and column_defaults errors from the railscast's solution but I get ActiveRecord::ConnectionNotEstablished error all the time when I try to write some attribute.

Any thoughts?

Tevis answered 1/9, 2011 at 19:31 Comment(0)
S
6

You should create your own model class and mix in the parts of ActiveModel (for example, validations) that you require. This blog post from Yehuda Katz has the details.

Scriven answered 1/9, 2011 at 20:13 Comment(1)
Thanks for response! Yeah, I've tried something like that but with no success. At first, I need a tableless model for testing some extension for ActiveRecord. And I need to use write_attribute for example. I think I can redefine write_attribute method or something like this. But RailsCast's solution works perfect for me in ActiveRecord <= 3.0.10 and I think there is a way how we can update this solution that will work in AR 3.1.Tevis
H
13

The simplest tableless model in Rails 3.1 is:

class Session
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :email, :password
  validates :email, :presence => true
  validates :password, :presence => true

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

  def persisted?
    false
  end
end

The ActiveModel::Validations is optional (only if validations are used). Also the constructor is not required (but highly desireable).

Hanschen answered 19/10, 2011 at 10:9 Comment(2)
this looks good, but how about someone wants to use relations without persistence?Papp
In Rails 3.2 you also have to add following lines: include ActiveModel::MassAssignmentSecurity attr_accessor :email, :passwordAmbassador
A
9

For Rails / ActiveRecord 5.0 you need to redefine private def self.load_schema! to avoid checking the table_name. Also, notice a little hack for the column method (Type).

Here's the complete listing for Tableless model for Rails 5.0 / ActiveRecord 5.0

class Tableless < ActiveRecord::Base

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

  def self.column(name, sql_type = nil, default = nil, null = true)
    type = "ActiveRecord::Type::#{sql_type.to_s.camelize}".constantize.new
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, type, 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

  # Override the save method to prevent exceptions.
  def save(validate = true)
    validate ? valid? : true
  end

  private

  def self.load_schema!
    columns_hash.each do |name, column|
      self.define_attribute(
        name,
        column.sql_type_metadata,
        default: column.default,
        user_provided_default: false
      )
    end
  end

end
Adenovirus answered 11/9, 2016 at 19:55 Comment(2)
Thanks for sharing your work, I could get rid of of activerecord-tableless which is not compatible with rails 5Pancreatotomy
@benj my saviorZymogenesis
P
7

This tableless thing seems more and more sort of a hack, but the mix just isn't the same thing (don't remember exactly what didn't work now, I've dealt with it some months ago, returned to it because upgrade to 3.1 broke it). The 3.1.0rc4 version worked with 'columns_hash' method override, the 3.1.0 requires also a 'column_defaults' override. So here's a version that passes my project tests.

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

  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

  # Override the save method to prevent exceptions.
  def save(validate = true)
    validate ? valid? : true
  end
end

Hope it works for you,

-- José

Purebred answered 4/9, 2011 at 3:39 Comment(1)
Thanks, José. But it doesn't work too :( I have the same error when I try to call write_attribute on my model: ActiveRecord::ConnectionNotEstablished Did you test it on rails 3.1 release?Tevis
S
6

You should create your own model class and mix in the parts of ActiveModel (for example, validations) that you require. This blog post from Yehuda Katz has the details.

Scriven answered 1/9, 2011 at 20:13 Comment(1)
Thanks for response! Yeah, I've tried something like that but with no success. At first, I need a tableless model for testing some extension for ActiveRecord. And I need to use write_attribute for example. I think I can redefine write_attribute method or something like this. But RailsCast's solution works perfect for me in ActiveRecord <= 3.0.10 and I think there is a way how we can update this solution that will work in AR 3.1.Tevis
K
1

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 to do it in Rails 3.x there is no support for association nor types.

Karate answered 7/9, 2012 at 12:43 Comment(0)
S
0

and For Rails 3.2 the version of RUBY should be preferred 1.9.3 to avoid incompatibilities.

Shingles answered 10/9, 2012 at 8:43 Comment(1)
@ Jari. It is nice gem to use for tableless model.Shingles

© 2022 - 2024 — McMap. All rights reserved.