rails 4-- model with no table
Just because you're using Rails, it doesn't mean every model has to inherit from ActiveRecord::Base
In your situation, I'd simply do something along these lines:
# app/models/feed.rb
class Feed
def initialize(feed_id)
@feed_id = feed_id
end
def fetch
# ... put the web-service handling here
end
# encapsulate the rest of your feed processing / handling logic here
end
# app/controllers/feeds_controller.rb
class FeedsController < ActionController::Base
def show
@feed = Feed.new(params[:feed_id])
@feed.fetch
# render
end
end
Remember: it's Ruby on Rails, not just Rails. You can do a lot more than just ActiveRecord models.
How to create ActiveRecord tableless Model in Rails 5?
Finally I have decided to left that code and move on. But with time I think it should be rewritten to relational solution or use JSON field.
Rails 5
class TableLess
include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Serialization
extend ActiveModel::Naming
class Error < StandardError;
end
module Type
class JSON < ActiveModel::Type::Value
def type
:json
end
private
def cast_value(value)
(value.class == String) ? ::JSON.parse(value) : value
end
end
class Symbol < ActiveModel::Type::Value
def type
:symbol
end
private
def cast_value(value)
(value.class == String || value.class == Symbol) ? value.to_s : nil
end
end
end
def initialize(attributes = {})
attributes = self.class.columns.map { |c| [c, nil] }.to_h.merge(attributes)
attributes.symbolize_keys.each do |name, value|
send("#{name}=", value)
end
end
def self.column(name, sql_type = :string, default = nil, null = true)
@@columns ||= {}
@@columns[self.name] ||= []
@@columns[self.name]<< name.to_sym
attr_reader name
caster = case sql_type
when :integer
ActiveModel::Type::Integer
when :string
ActiveModel::Type::String
when :float
ActiveModel::Type::Float
when :datetime
ActiveModel::Type::DateTime
when :boolean
ActiveModel::Type::Boolean
when :json
TableLess::Type::JSON
when :symbol
TableLess::Type::Symbol
when :none
ActiveModel::Type::Value
else
raise TableLess::Error.new('Type unknown')
end
define_column(name, caster, default, null)
end
def self.define_column(name, caster, default = nil, null = true)
define_method "#{name}=" do |value|
casted_value = caster.new.cast(value || default)
set_attribute_after_cast(name, casted_value)
end
end
def self.columns
@@columns[self.name]
end
def set_attribute_after_cast(name, casted_value)
instance_variable_set("@#{name}", casted_value)
end
def attributes
kv = self.class.columns.map {|key| [key, send(key)]}
kv.to_h
end
def persisted?
false
end
end
and example
class Machine < TableLess
column :foo, :integer
column :bar, :float
column :winamp, :boolean
end
ActiveRecord::Base Without Table
This is an approach I have used in the past:
In app/models/tableless.rb
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
# Override the save method to prevent exceptions.
def save(validate = true)
validate ? valid? : true
end
end
In app/models/foo.rb
class Foo < Tableless
column :bar, :string
validates_presence_of :bar
end
In script/console
Loading development environment (Rails 2.2.2)
>> foo = Foo.new
=> #<Foo bar: nil>
>> foo.valid?
=> false
>> foo.errors
=> #<ActiveRecord::Errors:0x235b270 @errors={"bar"=>["can't be blank"]}, @base=#<Foo bar: nil>>
Rails model without database
I think the blog post you are linking is the best way to go. I would only suggest moving the stubbed out methods into a module not to pollute your code.
Accessing join table (A table without model) in rails
There is no harm in creating a Model for a table in Rails. Though, if you wish to avoid it, you can use raw SQL queries for same.
sql = "SELECT * from posts where id = #{params[:id}"
result = ActiveRecord::Base.connection.execute(sql)
result.to_a
Ruby on Rails: models that do not have a table
Checkout the screencast by Ryan Bates that covers exactly this - Tableless Models.
http://railscasts.com/episodes/193-tableless-model
With this approach, your Model would still subclass ActiveRecord::Base
but define the columns manually, which allows you to use ActiveRecord
features such as validations, and associations without a database.
Rails association with tables without models
Your current scenario does require a model, although when you need to store data like categories e.g Sports(football,tennis,cricket,swimming,basketball,etc), you can store them as constants in your config->initializers->constant eg. SPORT_CATEGORIES = [["Football","football"],["Tennis","tennis"],etc], alternatively if you have more cols to store you can create a model then create the default rows as you would in a php .sql file but in ruby of course :) For example:
sport_categories = [
{:category => "football", :category_type => "manly" },
{:category => "Tennis", :category_type => "manly" },
etc
]
sport_categories.each do |attributes|
Model_name_here.find_or_initialize_by_category(attributes[:category]).tap do |p|
p.category_type = attributes[:category_type]
p.save!
end
end
Then you run rake db:seed.
Rails nested model conversion to and from database without its own table
Keep in mind that database adapters handle certain serialization tasks
for you. For instance: json and jsonb types in PostgreSQL will be
converted between JSON object/array syntax and Ruby Hash or Array
objects transparently. There is no need to use serialize in this case.
- api.rubyonrails.org
Don't use serialize
with native JSON/JSONB columns. Its meant to be used with string columns as a poor-mans alternative.
What you are trying to do is really outside the scope of what ActiveRecord does - AR is built around a relational model where models correspond to tables. And you cannot expect that AR will have any provisions to unmarshal a JSONB column into anything but basic scalar types. And I would consider if what you are doing is really worth the effort vs actually creating a separate table for the relation.
You are on the right track with ActiveModel::Model which will give your model the same behaviour as a regular model, but you should take a look at how ActiveRecord handles nested attributes:
class Rule < ApplicationRecord
def conditions_attributes=(attributes)
attributes.each do |a|
# you would need to implement this method
unless RuleCondition.reject_attributes?(a)
self.conditions << RuleCondition.new(c)
end
end
end
end
You can possibly mimic the other aspects of an association by creating setters/getters.
But then again you could just create a rule_conditions table with JSONB column and a one to many or m2m association and spend your time actually being productive instead.
Related Topics
How to Change All the Keys of a Hash by a New Set of Given Keys
Ruby on Rails - Drop Down Box on Change Event
How to Fix Rubygems Recent Deprecation Warning
Errno::Econnrefused: Connection Refused - Connect(2) for Action Mailer
Peer-To-Peer File Sharing with Web Sockets
How to Get the Name of the Command Called for Usage Prompts in Ruby
Ruby on Rails "Invalid Byte Sequence in Utf-8" Due to Bot
How to Combine Overlapping Time Ranges (Time Ranges Union)
How to Know When to "Refresh" My Model Object in Rails
How to Remove Permission Denied @ Rb_Sysopen - Gem Install Error
Ruby 1.9 + Sinatra Incompatible Character Encodings: Ascii-8Bit and Utf-8
Is There Goto Statement in Ruby