I have the below Fluentd plugin code:
require 'avro'
module Fluent
module TextFormatter
class Sample
end
class AvroFormatter < Formatter
Fluent::Plugin.register_formatter('avro', self)
config_param :schema_file, :string, :default => nil
config_param :schema_json, :string, :default => nil
def configure(conf)
super
if not (#schema_json.nil? ^ #schema_file.nil?) then
raise Fluent::ConfigError, 'schema_json or schema_file (but not both) is required'
end
if #schema_json.nil? then
#schema_json = File.read(#schema_file)
end
#schema = Avro::Schema.parse(#schema_json)
end
def format(tag, time, record)
handler = Sample.new()
end
end
end
end
And I need to instance the class "Sample" in the def "Format". The problem is that when I try to do a http POST against Fluentd the below error appears:
failed: error_class=NoMethodError error="undefined method `bytesize'
This error only appears when the class "Sample" is instanced. I'm new with ruby, and I don't know where is the problem. Should I create the class "Sample" in another file?
I think you're getting this error because code, that calls format expects string result, but instead it gets an instance of Sample class. Try to return some string instead.
You can also use this example here: http://docs.fluentd.org/articles/plugin-development#text-formatter-plugins.
I have used this method before in a Sinatra application with Datamapper without any troubles.
Now it doesn't seem to work. Any ideas appreciated.
My test:
scenario 'add hashtags to posts' do
visit '/'
add_post('Stacca',
'Hello! out there',
%w(foo bar))
post = Post.first
expect(post.hashtag.map(&:text)).to include('foo')
expect(post.hashtag.map(&:text)).to include('bar')
end
My server
post '/posting' do
username = params['username']
message = params['message']
hashtag = params['hashtag'].split(' ').map do |hashtag|
hashtag.first_or_create(text: hashtag)
end
Post.create(username: username, message: message, hashtag: hashtag)
redirect to ('/')
end
My Models:
class Post
include DataMapper::Resource
property :id, Serial
property :username, String
property :message, String
has n, :hashtag, through: Resource
end
and:
class Hashtag
include DataMapper::Resource
has n, :posts, through: Resource
property :id, Serial
property :text, String
end
Thank you
This line:
hashtag.first_or_create(text: hashtag)
should be:
Hashtag.first_or_create(text: hashtag) # uppercase!
Else, you are just trying to call a non existing "first_or_create" method on the String ("foo") you got from the scenario. 'Hashtag' is your class, 'hashtag' is your (String) variable.
Hashtag.first_or_create(text: hashtag)
Hashtag should have been a class
i.e. you missed the capital
I was using ActiveRecord validations, with custom error messages.The problem I came across is I want to show one more attribute in the error message. For example following code only shows value on which validation is running.
class Coffee < ActiveRecord::Base
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size" }
end
Can I also show type(assuming type is a field in Coffee table), following both attempts doesn't work:
class Coffee < ActiveRecord::Base
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} is not a valid size for type: %{type}" } #`method_missing': undefined local variable or method `type'
message: "%{value} is not a valid size for type: #{type}" } #Error: i18n::MissingInterpolationArgument
end
Versions:
ActiveRecord: 3.1.6
Ruby: ruby-1.9.3-p429
You can use lambda like this
class Coffee < ActiveRecord::Base
validates :size, inclusion: { in: %w(small medium large), :message=> lambda { |e| "#{e.size} is not a valid size for type #{e.type}"}
end
You can do this with a custom validation
class Coffee < ActiveRecord::Base
validate :size_for_type
def size_for_type
unless %w(small medium large).include?(size)
errors.add(:size, "%{value} is not a valid size for type: #{type}")
end
end
end
This came up a bit ago ( rails model attributes without corresponding column in db ) but it looks like the Rails plugin mentioned is not maintained ( http://agilewebdevelopment.com/plugins/activerecord_base_without_table ). Is there no way to do this with ActiveRecord as is?
If not, is there any way to get ActiveRecord validation rules without using ActiveRecord?
ActiveRecord wants the table to exist, of course.
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>>
Validations are simply a module within ActiveRecord. Have you tried mixing them into your non-ActiveRecord model?
class MyModel
include ActiveRecord::Validations
# ...
end
I figure the more answers the better since this is one of the first results in google when searching for "rails 3.1 models without tables"
I've implements the same thing without using ActiveRecord::Base while including the ActiveRecord::Validations
The main goal was to get everything working in formtastic, and below I've included a sample payment that will not get saved anywhere but still has the ability to be validated using the validations we all know and love.
class Payment
include ActiveModel::Validations
attr_accessor :cc_number, :payment_type, :exp_mm, :exp_yy, :card_security, :first_name, :last_name, :address_1, :address_2, :city, :state, :zip_code, :home_telephone, :email, :new_record
validates_presence_of :cc_number, :payment_type, :exp_mm, :exp_yy, :card_security, :first_name, :last_name, :address_1, :address_2, :city, :state
def initialize(options = {})
if options.blank?
new_record = true
else
new_record = false
end
options.each do |key, value|
method_object = self.method((key + "=").to_sym)
method_object.call(value)
end
end
def new_record?
return new_record
end
def to_key
end
def persisted?
return false
end
end
I hope this helps someone as I've spent a few hours trying to figure this out today.
UPDATE: For Rails 3 this can be done very easy. In Rails 3+ you can use the new ActiveModel module and its submodules. This should work now:
class Tableless
include ActiveModel::Validations
attr_accessor :name
validates_presence_of :name
end
For more info, you can check out the Railscast (or read about it on AsciiCasts) on the topic, as well as this blog post by Yehuda Katz.
OLD ANSWER FOLLOWS:
You may need to add this to the solution, proposed by John Topley in the previous comment:
class Tableless
class << self
def table_name
self.name.tableize
end
end
end
class Foo < Tableless; end
Foo.table_name # will return "foos"
This provides you with a "fake" table name, if you need one. Without this method, Foo::table_name will evaluate to "tablelesses".
Just an addition to the accepted answer:
Make your subclasses inherit the parent columns with:
class FakeAR < ActiveRecord::Base
def self.inherited(subclass)
subclass.instance_variable_set("#columns", columns)
super
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
# Overrides save to prevent exceptions.
def save(validate = true)
validate ? valid? : true
end
end
This is a search form that presents an object called criteria that has a nested period object with beginning and end attributes.
The action in the controller is really simple yet it loads values from nested objects on the form and re-renders the same values with error messages if necessary.
Works on Rails 3.1.
The model:
class Criteria < ActiveRecord::Base
class << self
def column_defaults
{}
end
def column_names
[]
end
end # of class methods
attr_reader :period
def initialize values
values ||= {}
#period = Period.new values[:period] || {}
super values
end
def period_attributes
#period
end
def period_attributes= new_values
#period.attributes = new_values
end
end
In the controller:
def search
#criteria = Criteria.new params[:criteria]
end
In the helper:
def criteria_index_path ct, options = {}
url_for :action => :search
end
In the view:
<%= form_for #criteria do |form| %>
<%= form.fields_for :period do |prf| %>
<%= prf.text_field :beginning_as_text %>
<%= prf.text_field :end_as_text %>
<% end %>
<%= form.submit "Search" %>
<% end %>
Produces the HTML:
<form action="/admin/search" id="new_criteria" method="post">
<input id="criteria_period_attributes_beginning_as_text" name="criteria[period_attributes][beginning_as_text]" type="text">
<input id="criteria_period_attributes_end_as_text" name="criteria[period_attributes][end_as_text]" type="text">
Note: The action attribute provided by the helper and the nested attributes naming format that makes it so simple for the controller to load all the values at once
There is the activerecord-tableless gem. It's a gem to create tableless ActiveRecord models, so it has support for validations, associations, types. It supports Active Record 2.3, 3.0, 3.2
The recommended way to do it in Rails 3.x (using ActiveModel) has no support for associations nor types.
Can any one volunteer why the class below fails?
... src/model/user.rb:18: undefined method `set_schema' for User:Class (NoMethodError)
I've looked in the Sequel-3.0 lib/ folder and the set_schema method is defined in a ClassMethods module.
I'm sure the solution is simple. I was thinking it should work "as is":
require 'sequel'
class User < Sequel::Model(:user)
set_schema do
set_primary_key :id
String :name
end
end
Recommended way ...
LOGGER = Object.new()
def LOGGER.method_missing( name, args )
puts "[#{name}] #{args}"
end
Sequel::Model.plugin(:schema) # I worked this out, but I can't find it documented
DB = Sequel.sqlite('sql_test.db', :loggers => [LOGGER] )
unless DB.table_exists?( :user )
DB.create_table :user do
set_primary_key :id
String :name
String :password
String :eMail
end #create_table
end #table exists
class User < Sequel::Model(:user)
The answer is to call up the plug-in for schema managing. Viz.
require 'sequel'
require 'logger'
LOGGER = Object.new()
def LOGGER.method_missing( name, args )
puts "[#{name}] #{args}"
end
**Sequel::Model.plugin(:schema)** # I still didn't find this documented
DB = Sequel.sqlite('sql_test.db', :loggers => [LOGGER] )
class User < Sequel::Model(:user)
set_schema do
set_primary_key :id
String :name
end
end
Yep Sequel::Model.plugin(:schema) worked for me too. Can't see it in the docs and I'm perplexed as to why, since I have another working project that uses set_schema without fuss.