Updating the database withe the migration file - ruby-on-rails

I have a simple user table and i would like to add a new field to my user table that says permalink. This permalink would be updated with the following code for all the users
name.downcase.gsub(/[^0-9a-z]+/, ' ').strip.gsub(' ', '-'). I want to create a migration file that updates all the users permalink fields with the code above so that old users would have their permalink set and i would use an after_create method for new users.

I think you could try something like this:
class User < ActiveRecord::Base
before_create :set_permalink
def set_permalink
permalink = name.downcase.gsub(/[^0-9a-z]+/, ' ').strip.gsub(' ', '-')
end
end
This actually uses the before_create callback, which would address the deriving of the permalink field for new users. I think this is what you actually need.
And in your migration file ...
class UpdateUsersPermalink < ActiveRecord::Migration
def self.up
User.reset_column_information
User.all.each do |u|
if u.permalink.nil?
u.set_permalink
u.save!
end
end
end
...
end
.. which would take care of any existing Users that do not have this field set just yet.

Related

Ruby ActiveRecord validation based on existing value in database

I know the common ways of validation in Ruby.
My current problem is, I would like the insert to fail if the record exists with boolean set to true. Example
Columns in table are => Name, Address, Has_changed, id
Now if has_changed is set to true in table, I would like to add new entry in the table (in a separate call) which will have name, Address (new one), has_changed(set to false) and id. I would not like to update existing entry as I want to keep history for the record.
Is there a way to have such a validation by using Ruby?
Thanks in advance.
I think its better use "new" and "create" actions in your controller
class RecordsController < BaseController
def new
#record = Record.find(params[:id])
end
def create
Record.create(params[:record])
end
end
Your form just contains adress
In your model make a before_create hook
class Record < ActiveRecord::Base
default_value_for :has_changed, false
before_create :check_changed
...
private
def check_changed
changed_record = Record.where(name: name, has_changed: false).first
if changed_record && (changed_record.address != address)
changed_record.has_changed = true
changed_record.save
end
end
end
And add a validation for address to avoid duplication

Rails sha1 hash generator

Hi hopefully somebody can help me out. I'm a bit stuck at the moment. I'm trying to create an app for a tracking system, I currently have a table called sdel_hashed. Following online videos I so far set up digest/sha1 to work partly. If I enter the following commands in the console:
sdel = Sdel.find(1)
sdel.hashed_sdel = Sdel.hash('secret')
sdel.save
And then view the record in the browser it show up as the hash and not secret, but if I try and enter the word secret through the new action it doesn't get hashed. I think there is maybe something missing in the create action but I cannot find answers anywhere. i would greatly appreciate any help. I'll include now what I have in my controller and model.
Thanks
model sdel
require 'digest/sha1'
class Sdel < ActiveRecord::Base
attr_accessible :hashed_sdel
def self.hash(sdel="")
Digest::SHA1.hexdigest(sdel)
end
end
controller sdels
class SdelsController < ApplicationController
def list
#sdel = Sdel.all
end
def new
#sdel = Sdel.new
end
def create
#sdel = Sdel.new(params[:sdel])
if #sdel.save
redirect_to(:action => 'list')
else
render('new')
end
end
end
Migration file
class CreateSdels < ActiveRecord::Migration
def change
create_table :sdels do |t|
t.string "hashed_sdel"
t.timestamps
end
end
end
Sounds like you may want to use a before_save filter to invoke the hash class method on the Sdel model prior to saving when the attribute has been modified. Perhaps something along the lines of this:
require 'digest/sha1'
class Sdel < ActiveRecord::Base
attr_accessible :hashed_sdel
before_save { self.hashed_sdel = self.class.hash(hashed_sdel) if hashed_sdel_changed? }
def self.hash(sdel="")
Digest::SHA1.hexdigest(sdel)
end
end
This way, if you have a form that has a text_field for your hashed_sdel attribute, it will automatically get run through the hash class method you have before it the record gets saved (assuming the attribute has changed from it's previous value).

Creating a Rails change log

I am pretty new to rails (and development) and have a requirement to create a change log. Let's say you have an employees table. On that table you have an employee reference number, a first name, and a last name. When either the first name or last name changes, I need to log it to a table somewhere for later reporting. I only need to log the change, so if employee ref 1 changes from Bill to Bob, then I need to put the reference number and first name into a table. The change table can have all the columns that mnight change, but most only be populated with the reference number and the changed field. I don't need the previous value either, just the new one. hope that makes sense.
Looked at gems such as paper trail, but they seem very complicated for what I need. I don't ever need to manipulate the model or move versions etc, I just need to track which fields have changed, when, and by whom.
I'd appreciate your recommendations.
If you insist on building your own changelog, based on your requirements you can do so using a few callbacks. First create your log table:
def up
create_table :employee_change_logs do |t|
t.references :employee
# as per your spec - copy all column definitions from your employees table
end
end
In your Employee model:
class Employee < ActiveRecord::Base
has_many :employee_change_logs
before_update :capture_changed_columns
after_update :log_changed_columns
# capture the changes before the update occurs
def capture_changed_columns
#changed_columns = changed
end
def log_changed_columns
return if #changed_columns.empty?
log_entry = employee_change_logs.build
#changed_columns.each{|c| log_entry.send(:"#{c}=", self.send(c))}
log_entry.save!
end
end
I recommend the gem vestal_versions.
To version an ActiveRecord model, simply add versioned to your class like so:
class User < ActiveRecord::Base
versioned
validates_presence_of :first_name, :last_name
def name
"#{first_name} #{last_name}"
end
end
And use like this:
#user.update_attributes(:last_name => "Jobs", :updated_by => "Tyler")
#user.version # => 2
#user.versions.last.user # => "Tyler"
The first thing we did was put an around filter in the application controller. This was how I get the current_employee into the employee model, which was the challenge, especially for a newbie like me!
around_filter :set_employee_for_log, :if => Proc.new { #current_account &&
#current_account.log_employee_changes? && #current_employee }
def set_employee_for_log
Thread.current[:current_employee] = #current_employee.id
begin
yield
ensure
Thread.current[:current_employee ] = nil
end
end
end
Next, in the employee model I defined which fields I was interested in monitoring
CHECK_FIELDS = ['first_name', 'last_name', 'middle_name']
then I added some hooks to actually capture the changes IF logging is enabled at the account level
before_update :capture_changed_columns
after_update :log_changed_columns, :if => Proc.new { self.account.log_employee_changes? }
def capture_changed_columns
#changed_columns = changed
#changes = changes
end
def log_changed_columns
e = EmployeeChangeLog.new
Employee::CHECK_FIELDS.each do |field|
if self.send("#{field}_changed?")
e.send("#{field}=", self.send(field))
end
end
if e.changed?
e.update_attribute(:account_id, self.account.id)
e.update_attribute(:employee_id, self.id)
e.update_attribute(:employee_ref, self.employee_ref)
e.update_attribute(:user_id, Thread.current[:current_employee])
e.save
else return
end
end
And that;s it. If the account enables it, the app keeps an eye on specific fields and then all changes to those fields are logged to a table, creating an simple audit trail.

Using strip before field gets saved to databse in ROR

I would like to strip all whitespace in some fields before they go into my database.
I am using devise and have added additional fields to the members table (used members instead of users).
On my sign up form I have some fields such as telephone and address however I would like to strip all whitespace for certain fields like :telephone, :mobile and :emergency_number.
Sounds like a job for before_save!
class Member < ActiveRecord::Base
before_save :strip_whitespace
private
def strip_whitespace
self.telephone.gsub!(/\s+/, '')
# etc...
end
end
An easy way to #Chowlett's solution
class Member < ActiveRecord::Base
before_save :strip_whitespace
private:
def strip_whitespace
self.telephone.join('')
# etc...
end
end

Rails 3 add virtual attribute dynamically

My setup: Rails 3.0.9, Ruby 1.9.2
I have my reasons for doing this but what I need is a way to add a virtual attribute to an activerecord resultset dynamically. That means I am not using attr_accessor in the model and instead wish to dynamically add virtual attributes to a resultset.
For example,
users = User.all
#a user has following attributes: name, email, password
What I like to do is say add (without using attr_accessor) a virtual attribute status to users, is that possible?
You should do this:
users.each do |user|
user.instance_eval do
def status
instance_variable_get("#status")
end
def status=(val)
instance_variable_set("#status",val)
end
end
end
you can do the following:
add an attribute "extras" which will be accessed as a Hash, and which will store any additional / dynamic attributes -- and then tell Rails to persist that Hash via JSON in ActiveRecord or Mongo or whatever you use
e.g.:
class AddExtrasToUsers < ActiveRecord::Migration
def self.up
add_column :users, :extras, :text # do not use a binary type here! (rails bug)
end
...
end
then in the model add a statement to "serialize" that new attribute -- e.g. that means it's persisted as JSON
class User < ActiveRecord::Base
...
serialize :extras
...
end
You can now do this:
u = User.find 3
u.extras[:status] = 'valid'
u.save
You can also add a bit of magic to the User model, to look at the extras Hash if it gets a method_missing() call
See also:
Google "Rails 3 serialize"
If your attributes are read-only, you can also add them by selecting them in your database query. Fields which appear in the SQL result result will automatically be add as attributes.
Examples:
User.find_by_sql('users.*, (...) AS status')
User.select('users.*, joined_table.status').join(...)
With these examples you'll have all User attributes and an additional status attribute.
You could simply do:
users.each { |user| user.class_eval { attr_accessor :status } }
All users will have user.status and user.status = :new_status available.

Resources