I have a method where an object is called but I can not seem to pull out the attributes of this object and I do not understand why.
def set_cashier
test = User.first
result = test.login
Rails.logger.debug "User is: #{result}"
as I set a breakpoint on the second line in rubymine IDE (I can see the following)
def set_cashier
test = User.first test: #<User:0x00000004809ea0>
result = test.login result: nil test: #<User:0x00000004809ea0>
Rails.logger.debug "User is: #{result}"
I know I have attributes on this object like id and login. When I run the debugger in my IDE I can see #attributes = Hash(17 elements) and I can see them listed inside as 'id' = "433" and 'login' = "firstname.lastname" etc...in the rubymine debugger it looks sort of like this...
result = nil
test = {User}#<User:0x00000004809ea0>
#attributes = Hash(17 elements)
'id' = "433"
'login' = "firstname.lastname"
...
How do I return the value of 'login'?
test = User.first seems to give an object that I can see in the debugger Variables tab that I can open and see a value for "#attributes = Hash(17 elements)" and I can see those values inside there....and yet...
"result = test.login" gives a nil result which so confusing
I would think that "test = User.first.login" should work ...
def set_cashier
test = User.first.login
result = test
Rails.logger.debug "User.first is: #{result}"
but this gives the same error ...so confusing.
(the full error displayed in the browser looks like so...)
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map
I've been at this for a few days now. If this question does not make sense please let me know.
Thank you for your time.
UPDATED:
Per request of a commenter I'm including the code for the User model
(By the way...I inherited this code...so nothing in this file was written by me)
Note:
Rails 3.1 (updated from a rails 2.x app)
ruby 1.9.3p551
user.rb
class User < ActiveRecord::Base
acts_as_userstamp
has_and_belongs_to_many "roles"
# Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :login, :email
validates_presence_of :password, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_length_of :password, :within => 4..40, :if => :password_required?, :allow_nil => true
validates_confirmation_of :password, :if => :password_required?
validates_length_of :login, :within => 3..40, :allow_nil => true
validates_length_of :email, :within => 3..100, :allow_nil => true
validates_uniqueness_of :login, :email, :case_sensitive => false
validates_uniqueness_of :cashier_code, :if => :cashier_code
validates_format_of :login, :with => /[^0-9]/, :message => "must contain a non-numeric character"
before_save :encrypt_password
before_save :add_cashier_code
before_save :disable_reason_cannot_login_on_reenable
def disable_reason_cannot_login_on_reenable
return unless self.can_login && self.can_login_changed?
self.reason_cannot_login = "" if self.reason_cannot_login && self.reason_cannot_login.length > 0
end
belongs_to :contact
has_one :skedjulnator_access
####################################################
# I HAVE NO IDEA WHAT THIS IS HERE FOR, BUT IF YOU #
# FORGET ABOUT IT YOU WILL SPEND AN HOUR TRYING TO #
# FIGURE OUT WHAT YOU DID WRONG #
####################################################
# prevents a user from submitting a crafted form that bypasses activation
# anything else you want your user to change should be added here.
attr_accessible :login, :email, :password, :password_confirmation, :can_login, :shared
scope :can_login, {:conditions => ["can_login = 't'"]}
def self.hidden_columns
super + [:crypted_password, :salt]
end
def can_view_disciplinary_information?
!! (self.contact and self.contact.worker and self.contact.worker.worker_type_today and self.contact.worker.worker_type_today.name != 'inactive')
end
def update_skedjulnator_access_time
self.skedjulnator_access ||= SkedjulnatorAccess.new
self.skedjulnator_access.user_id_will_change!
self.skedjulnator_access.save!
end
def grantable_roles
self.roles.include?(Role.find_by_name('ADMIN')) ? Role.find(:all) : self.roles
end
def to_s
login
end
def self.reset_all_cashier_codes
self.find(:all).each{|x|
x.reset_cashier_code
x.save
}
end
def contact_display_name
self.contact ? self.contact.display_name : self.login
end
def add_cashier_code
reset_cashier_code if !self.shared and cashier_code.nil?
end
def reset_cashier_code
valid_codes = (1000..9999).to_a - User.find(:all).collect{|x| x.cashier_code}
my_code = valid_codes[rand(valid_codes.length)]
self.cashier_code = my_code
end
def merge_in(other)
for i in [:actions, :donations, :sales, :types, :users, :volunteer_tasks, :contacts, :gizmo_returns]
User.connection.execute("UPDATE #{i.to_s} SET created_by = #{self.id} WHERE created_by = #{other.id}")
User.connection.execute("UPDATE #{i.to_s} SET updated_by = #{self.id} WHERE updated_by = #{other.id}")
end
["donations", "sales", "volunteer_tasks", "disbursements", "recyclings", "contacts"].each{|x|
User.connection.execute("UPDATE #{x.to_s} SET cashier_created_by = #{self.id} WHERE cashier_created_by = #{other.id}")
User.connection.execute("UPDATE #{x.to_s} SET cashier_updated_by = #{self.id} WHERE cashier_updated_by = #{other.id}")
}
self.roles = (self.roles + other.roles).uniq
self.save!
end
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
def self.authenticate(login, password)
if login.to_i.to_s == login
u = find_by_contact_id(login.to_i)
else
u = find_by_login(login) # need to get the salt
end
return u if u && u.can_login && u.authenticated?(password)
return nil
end
# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
def authenticated?(password)
crypted_password == encrypt(password)
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
remember_me_for 2.weeks
end
def remember_me_for(time)
remember_me_until time.from_now.utc
end
def remember_me_until(time)
self.remember_token_expires_at = time
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
# start auth junk
def User.current_user
Thread.current['user'] || User.fake_new
end
attr_accessor :fake_logged_in
def User.fake_new
u = User.new
u.fake_logged_in = true
u
end
def logged_in
! fake_logged_in
end
def to_privileges
return "logged_in" if self.logged_in
end
def privileges
#privileges ||= _privileges
end
def _privileges
olda = []
return olda if !self.can_login
a = [self, self.contact, self.contact ? self.contact.worker : nil, self.roles].flatten.select{|x| !x.nil?}.map{|x| x.to_privileges}.flatten.select{|x| !x.nil?}.map{|x| Privilege.by_name(x)}
while olda != a
a = a.select{|x| !x.restrict} if self.shared
olda = a.dup
a << olda.map{|x| x.children}.flatten
a = a.flatten.sort_by(&:name).uniq
a = a.select{|x| !x.restrict} if self.shared
end
a = a.map{|x| x.name}
a
end
def has_privileges(*privs)
positive_privs = []
negative_privs = []
privs.flatten!
for i in privs
if i.match(/^!/)
negative_privs << i.sub(/^!/, "")
else
positive_privs << i
end
end
if positive_privs.length > 0
positive_privs << "role_admin"
end
if negative_privs.length > 0
negative_privs << "role_admin"
end
my_privs = self.privileges
#puts "NEG: #{negative_privs.inspect}, POS: #{positive_privs.inspect}, MY: #{my_privs.inspect}"
return (negative_privs & my_privs).length == 0 && ((positive_privs & my_privs).length > 0 || positive_privs.length == 0)
end
# end auth junk
protected
# before filter
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
self.crypted_password = encrypt(password)
end
def password_required?
crypted_password.blank? || !password.blank?
end
end
Eliminating the IDE as source of problem:
So I ran Rails console and got the following error...
irb(main):001:0> User.first
User Load (0.5ms) SELECT "users".* FROM "users" LIMIT 1
(0.1ms) SHOW search_path
User Indexes (1.1ms) SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
WHERE i.relkind = 'i'
AND d.indisprimary = 'f'
AND t.relname = 'users'
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN ('"$user"','public') )
ORDER BY i.relname
User Indexes (0.4ms) SELECT c2.relname, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
WHERE c.relname = 'users'
AND c.oid = i.indrelid AND i.indexrelid = c2.oid
AND i.indisprimary = 'f'
AND i.indexprs IS NOT NULL
ORDER BY 1
(Object doesn't support #inspect)
=>
irb(main):002:0>
...very confusing
Related
I've got TWO apps...an old Rails 2.3.14 app and the same code updated to Rails 3.1.0. (I know 6.x is recent but I'm taking small steps first)
I'm testing both apps against the same postgres database in turns
When I run the 2.3 app in console and pull up Rails console I get a result...
>> User.first
=> #<User id: 433, login: "danny.vally", email: "danny.vally#gmail.com", crypted_password: "xxxxxxxxxxxxxxxxxxxxxxx", salt: "yyyyyyyyyyyyyyyyyyyyyyyy", created_at: "2015-12-11 10:56:17", updated_at: "2016-10-01 00:00:23", remember_token: nil, remember_token_expires_at: nil, contact_id: 1222222, created_by: 333, updated_by: 434, cashier_code: 9555, can_login: false, last_logged_in: "2016-03-05", shared: false, reason_cannot_login: "Account disabled due to 280 days of inactivity.">
Great...I can see the DB and see a user in the User model.
Now, on the Rails 3.1.0 version (pointing to the same DB...after exiting the other app)....I get the following in rails console...
$ rails c
/home/fonso/.rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/soap4r-ruby1.9-2.0.5/lib/xsd/iconvcharset.rb:9:in `<top (required)>': iconv will be deprecated in the future, use String#encode instead.
/home/fonso/.rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/12_hour_time-0.0.4/lib/12_hour_time/action_view_helpers.rb:3: warning: already initialized constant POSITION
Loading development environment (Rails 3.1.0)
irb(main):001:0> User.first
User Load (0.7ms) SELECT "users".* FROM "users" LIMIT 1
(0.1ms) SHOW search_path
User Indexes (1.1ms) SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
INNER JOIN pg_class i ON d.indexrelid = i.oid
WHERE i.relkind = 'i'
AND d.indisprimary = 'f'
AND t.relname = 'users'
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN ('"$user"','public') )
ORDER BY i.relname
User Indexes (0.4ms) SELECT c2.relname, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true)
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
WHERE c.relname = 'users'
AND c.oid = i.indrelid AND i.indexrelid = c2.oid
AND i.indisprimary = 'f'
AND i.indexprs IS NOT NULL
ORDER BY 1
(Object doesn't support #inspect)
=>
irb(main):002:0>
Here is the User model is that helps ...
class User < ActiveRecord::Base
Rails.logger.debug "User Model called"
acts_as_userstamp
has_and_belongs_to_many :roles
# Virtual attribute for the unencrypted password
attr_accessor :password
validates_presence_of :login, :email
validates_presence_of :password, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_length_of :password, :within => 4..40, :if => :password_required?, :allow_nil => true
validates_confirmation_of :password, :if => :password_required?
validates_length_of :login, :within => 3..40, :allow_nil => true
validates_length_of :email, :within => 3..100, :allow_nil => true
validates_uniqueness_of :login, :email, :case_sensitive => false
validates_uniqueness_of :cashier_code, :if => :cashier_code
validates_format_of :login, :with => /[^0-9]/, :message => "must contain a non-numeric character"
before_save :encrypt_password
before_save :add_cashier_code
before_save :disable_reason_cannot_login_on_reenable
def disable_reason_cannot_login_on_reenable
return unless self.can_login && self.can_login_changed?
self.reason_cannot_login = "" if self.reason_cannot_login && self.reason_cannot_login.length > 0
end
belongs_to :contact
has_one :skedjulnator_access
# attr_accessible :login, :email, :password, :password_confirmation, :can_login, :shared
# S.O. says this line will not work for Rails 3.1
# https://stackoverflow.com/questions/68291144/can-not-seem-to-pull-out-the-attributes-of-this-object
scope :can_login, {:conditions => ["can_login = 't'"]}
def self.hidden_columns
super + [:crypted_password, :salt]
end
def can_view_disciplinary_information?
!! (self.contact and self.contact.worker and self.contact.worker.worker_type_today and self.contact.worker.worker_type_today.name != 'inactive')
end
def update_skedjulnator_access_time
self.skedjulnator_access ||= SkedjulnatorAccess.new
self.skedjulnator_access.user_id_will_change!
self.skedjulnator_access.save!
end
def grantable_roles
self.roles.include?(Role.find_by_name('ADMIN')) ? Role.find(:all) : self.roles
end
def self.reset_all_cashier_codes
self.find(:all).each{|x|
x.reset_cashier_code
x.save
}
end
def contact_display_name
self.contact ? self.contact.display_name : self.login
end
def add_cashier_code
reset_cashier_code if !self.shared and cashier_code.nil?
end
def reset_cashier_code
valid_codes = (1000..9999).to_a - User.find(:all).collect{|x| x.cashier_code}
my_code = valid_codes[rand(valid_codes.length)]
self.cashier_code = my_code
end
def merge_in(other)
for i in [:actions, :donations, :sales, :types, :users, :volunteer_tasks, :contacts, :gizmo_returns]
User.connection.execute("UPDATE #{i.to_s} SET created_by = #{self.id} WHERE created_by = #{other.id}")
User.connection.execute("UPDATE #{i.to_s} SET updated_by = #{self.id} WHERE updated_by = #{other.id}")
end
["donations", "sales", "volunteer_tasks", "disbursements", "recyclings", "contacts"].each{|x|
User.connection.execute("UPDATE #{x.to_s} SET cashier_created_by = #{self.id} WHERE cashier_created_by = #{other.id}")
User.connection.execute("UPDATE #{x.to_s} SET cashier_updated_by = #{self.id} WHERE cashier_updated_by = #{other.id}")
}
self.roles = (self.roles + other.roles).uniq
self.save!
end
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
def self.authenticate(login, password)
if login.to_i.to_s == login
u = find_by_contact_id(login.to_i)
else
u = find_by_login(login) # need to get the salt
end
return u if u && u.can_login && u.authenticated?(password)
return nil
end
# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
def authenticated?(password)
crypted_password == encrypt(password)
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
remember_me_for 2.weeks
end
def remember_me_for(time)
remember_me_until time.from_now.utc
end
def remember_me_until(time)
self.remember_token_expires_at = time
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
save(false)
end
def forget_me
self.remember_token_expires_at = nil
self.remember_token = nil
save(false)
end
# start auth junk
def User.current_user
Thread.current['user'] || User.fake_new
end
attr_accessor :fake_logged_in
def User.fake_new
u = User.new
u.fake_logged_in = true
u
end
def logged_in
! fake_logged_in
end
def to_privileges
return "logged_in" if self.logged_in
end
def privileges
#privileges ||= _privileges
end
def _privileges
olda = []
return olda if !self.can_login
a = [self, self.contact, self.contact ? self.contact.worker : nil, self.roles].flatten.select{|x| !x.nil?}.map{|x| x.to_privileges}.flatten.select{|x| !x.nil?}.map{|x| Privilege.by_name(x)}
while olda != a
a = a.select{|x| !x.restrict} if self.shared
olda = a.dup
a << olda.map{|x| x.children}.flatten
a = a.flatten.sort_by(&:name).uniq
a = a.select{|x| !x.restrict} if self.shared
end
a = a.map{|x| x.name}
a
end
def has_privileges(*privs)
positive_privs = []
negative_privs = []
privs.flatten!
for i in privs
if i.match(/^!/)
negative_privs << i.sub(/^!/, "")
else
positive_privs << i
end
end
if positive_privs.length > 0
positive_privs << "role_admin"
end
if negative_privs.length > 0
negative_privs << "role_admin"
end
my_privs = self.privileges
#puts "NEG: #{negative_privs.inspect}, POS: #{positive_privs.inspect}, MY: #{my_privs.inspect}"
return (negative_privs & my_privs).length == 0 && ((positive_privs & my_privs).length > 0 || positive_privs.length == 0)
end
# end auth junk
protected
# before filter
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
self.crypted_password = encrypt(password)
end
def password_required?
crypted_password.blank? || !password.blank?
end
end
I've been pouring over this for weeks but I'm stumped. Is there any hope? Is this some sort of glitch in 3.1.0 that might have been fixed in some later version? Maybe 3.1.1? Or is it impossible to upgrade from 2.3.14 to 3.1.0?
Thank you for your time.
I'm new in Ruby. I want to create different users in ruby using iteration.
def createuser(*args)
obj = H['userClass']
obj.login = H['login']
obj.password = a.password = #default_passwd
obj.email = 'test#example.com'
obj.role = MasterUser::ROLE_MASTER_USER
end
For example I want to call this method and send these arguments:
H = Hash["userClass" => MasterUser.new, "login" => admin]
createuser(H)
What is the proper way to implement this?
Here's a modified version. It should bring you closer to your goal, while still being recognizable :
def create_user(parameters)
klass = parameters['user_class']
user = klass.new
user.login = parameters['login']
user.password = #default_passwd
user.email = 'test#example.com'
user.role = klass::ROLE_MASTER_USER
user
end
user_params = {"user_class" => MasterUser, "login" => 'admin'}
new_user = create_user(user_params)
I'd probably do something like this:
class UserFactory
attr_accessor :user
def initialize(klass)
#user = klass.new
end
def create(params = {})
user.login = params.fetch :login
user.password = params.fetch :password, 'default_password'
user.email = params.fetch :email
# user.role should just be initialised on the klass.new call, no need to do that here
# etc...
end
end
class MasterUser
ROLE = 'master_role'
attr_accessor :login, :password, :email, :role
def initialize
self.role = ROLE
end
end
which you would call like:
UserFactory.new(MasterUser).create(login: 'george', password: 'secret', email: 'me#george.com')
The reason I'd use params.fetch :login, instead of just reading it, is that in Ruby accessing a hash by a key that it doesn't have returns nil, while trying to fetch it will throw an error.
For example:
a = {}
a[:foo] #=> nil
a.fetch :foo #=> throw a KeyError
So that is a way of enforcing that the argument hash has the right keys.
When I go to /messages?mailbox=sent I get a "undefined method `sent_messages'" In the messages controller I have under def index #user.sent_messages. So I am not sure as to why it is undefined.
message model:
attr_accessible :subject, :body, :sender_id, :recepient_id, :read_at,:sender_deleted,:recepient_deleted
validates_presence_of :subject, :message => "Please enter message title"
belongs_to :sender,
:class_name => 'User',
:foreign_key => 'sender_id'
belongs_to :recepient,
:class_name => 'User',
:foreign_key => 'recepient_id'
# marks a message as deleted by either the sender or the recepient, which ever the user that was passed is.
# When both sender and recepient marks it deleted, it is destroyed.
def mark_message_deleted(id,user_id)
self.sender_deleted = true if self.sender_id == user_id and self.id=id
self.recepient_deleted = true if self.recepient_id == user_id and self.id=id
self.sender_deleted && self.recepient_deleted ? self.destroy : save!
end
# Read message and if it is read by recepient then mark it is read
def self.readingmessage(id, reader)
message = find(id, :conditions => ["sender_id = ? OR recepient_id = ?", reader, reader])
if message.read_at.nil? && (message.recepient.user_id==reader)
message.read_at = Time.now
message.save!
end
message
end
# Based on if a message has been read by it's recepient returns true or false.
def read?
self.read_at.nil? ? false : true
end
user model:
def unread_messages?
unread_message_count > 0 ? true : false
end
# Returns the number of unread messages for this user
def unread_message_count
eval 'messages.count(:conditions => ["recepient_id = ? AND read_at IS NULL", self.user_id])'
end
def to_s; username
end
def has_role?(role_name)
role.present? && role.to_sym == role_name.to_sym
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
I think you don't have sent_messages method in User model. Define below method in your user.rb.
def sent_messages
#Your code
end
Could you provide exactly what you looking for? So it is easy to help and you can get more appropriate answer.
EDIT:
As I see you don't have any instance level method called sent_messages in User model.
I'm trying to create a form using textarea and a submit button that will allow users to do bulk insert. For example, the input would look like this:
0001;MR A
0002;MR B
The result would look like this:
mysql> select * from members;
+------+------+------+
| id | no | name |
+------+------+------+
| 1 | 0001 | MR A |
+------+------+------+
| 2 | 0002 | MR B |
+------+------+------+
I'm very new to Rails and I'm not sure on how to proceed with this one. Should I use attr_accessor? How do I handle failed validations in the form view? Is there any example? Thanks in advance.
Update
Based on MissingHandle's comment, I created a Scaffold and replace the Model's code with this:
class MemberBulk < ActiveRecord::Base
attr_accessor :member
def self.columns
#columsn ||= []
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
column :data, :text
validates :data, :create_members, :presence => true
def create_members
rows = self.data.split("\r\n")
#member = Array.new
rows.each_with_index { |row, i|
rows[i] = row.strip
cols = row.split(";")
p = Member.new
p.no = cols[0]
p.name = cols[1]
if p.valid?
member << p
else
p.errors.map { |k, v| errors.add(:data, "\"#{row}\" #{v}") }
end
}
end
def create_or_update
member.each { |p|
p.save
}
end
end
I know the code is far from complete, but I need to know is this the correct way to do it?
class MemberBulk < ActiveRecord::Base
#Tells Rails this is not actually tied to a database table
# or is it self.abstract_class = true
# or #abstract_class = true
# ?
abstract_class = true
# members holds array of members to be saved
# submitted_text is the data submitted in the form for a bulk update
attr_accessor :members, :submitted_text
attr_accessible :submitted_text
before_validation :build_members_from_text
def build_members_from_text
self.members = []
submitted_text.each_line("\r\n") do |member_as_text|
member_as_array = member_as_text.split(";")
self.members << Member.new(:number => member_as_array[0], :name => member_as_array[1])
end
end
def valid?
self.members.all?{ |m| m.valid? }
end
def save
self.members.all?{ |m| m.save }
end
end
class Member < ActiveRecord::Base
validates :number, :presence => true, :numericality => true
validates :name, :presence => true
end
So, in this code, members is an array that is a collection of the individual Member objects. And my thinking is that as much as possible, you want to hand off work to the Member class, as it is the class that will actually be tied to a database table, and on which you can expect standard rails model behavior. In order to accomplish this, I override two methods common to all ActiveRecord models: save and valid. A MemberBulk will only be valid if all it's members are valid and it will only count as saved if all of it's members are saved. You should probably also override the errors method to return the errors of it's underlying members, possibly with an indication of which one it is in the submitted text.
In the end I had to change from using Abstract Class to Active Model (not sure why, but it stoppped working the moment I upgrade to Rails v3.1). Here's the working code:
class MemberBulk
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :input, :data
validates :input, presence: true
def initialize(attributes = {})no
attributes.each do |name, value|
send("#{name}=", value) if respond_to?("#{name}=")
end
end
def persisted?
false
end
def save
unless self.valid?
return false
end
data = Array.new
# Check for spaces
input.strip.split("\r\n").each do |i|
if i.strip.empty?
errors.add(:input, "There shouldn't be any empty lines")
end
no, nama = i.strip.split(";")
if no.nil? or nama.nil?
errors.add(:input, "#{i} doesn't have no or name")
else
no.strip!
nama.strip!
if no.empty? or nama.empty?
errors.add(:input, "#{i} doesn't have no or name")
end
end
p = Member.new(no: no, nama: nama)
if p.valid?
data << p
else
p.errors.full_messages.each do |error|
errors.add(:input, "\"#{i}\": #{error}")
end
end
end # input.strip
if errors.empty?
if data.any?
begin
data.each do |d|
d.save
end
rescue Exception => e
raise ActiveRecord::Rollback
end
else
errors.add(:input, "No data to be processed")
return false
end
else
return false
end
end # def
end
I am using Ruby on Rails 3 and I would like to inizialize an ActiveRecord Tableless Model.
In my model I have:
class Account < ActiveRecord::Base
# The following ActiveRecord Tableless Model statement is from http://codetunes.com/2008/07/20/tableless-models-in-rails/
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_reader :id,
:firstname,
:lastname,
def initialize(attributes = {})
#id = attributes[:id]
#firstname = attributes[:firstname]
#lastname = attributes[:lastname]
end
end
If in a controller, for example in the application_controller.rb file, I do:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
a debug\inspect output of the #new_account variable is
"#<Account >"
Why? How I should inizialize properly that ActiveRecord Tableless Model and make it to work?
According to that blog post it would have to look like this:
class Account < ActiveRecord::Base
class_inheritable_accessor :columns
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
column :id, :integer
column :firstname, :string
column :lastname, :string
end
And then:
#new_account = Account.new({:id => "1", :firstname => "Test name", :lastname => "Test lastname"})
Did you already try it like that?
I my view, you don't need to extend ActiveRecord::Base class.
You can write your own model class something like this
# models/letter.rb
class Letter
attr_reader :char
def self.all
('A'..'Z').map { |c| new(c) }
end
def self.find(param)
all.detect { |l| l.to_param == param } || raise(ActiveRecord::RecordNotFound)
end
def initialize(char)
#char = char
end
def to_param
#char.downcase
end
def products
Product.find(:all, :conditions => ["name LIKE ?", #char + '%'], :order => "name")
end
end
# letters_controller.rb
def index
#letters = Letter.all
end
def show
#letter = Letter.find(params[:id])
end
I hope it will help you.
Reference: http://railscasts.com/episodes/121-non-active-record-model