Dynamically creating models for a table in rails - ruby-on-rails

I have a migration that will dynamically create tables on fly per date. Something like this:
class CreateCollectorPeriodTable < ActiveRecord::Migration
def self.create_with(name)
create_table name.to_sym do |t|
t.string :text, :limit => 1024
end
end
end
I want to create a model that will access this migration..
I did read this: Rails Generate Model from Existing Table?, but in another question someone explained why I shouldn't try and make one model fit many tables..
Any suggestions?

class CreateCollectorPeriodTable < ActiveRecord::Migration
# name should be plural
# i.e.: name = 'chickens'
def self.create_with(name)
create_table name.to_sym do |t|
t.string :text, :limit => 1024
end
model_file = File.join("app", "models", name.singularize+".rb")
model_name = name.singularize.capitalize
File.open(model_file, "w+") do |f|
f << "class #{model_name} < ActiveRecord::Base\nend"
end
end
end

Related

Translations for each model

In my domain, many models have names, descriptions, etc. These properties need translations. I know how to represent this in a database. However I struggle finding a way to represent this with Rails.
|-------translations-table--------|
|translation_id|locale|translation|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|----------------------modelx-table---------------------|
|id|name_translation_id|description_translation_id|price|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|-------modely-table--------|
|id|name_translation_id|date|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You do not need to create extra models for translations, yo just need to set up locales in .yml format, check this for further instructions
Update
Ok now I understood your point, you want to add translatable fields on your entities/models, so users can manage those translations through a UI right?, well your approach is correct, however there is a gem called Globalize that does the exact same thing but with more toys, and much more standardized as you want.
This is the solution I eventually came up with:
#Models
class Translation
has_many :translation_records
end
class TranslationRecord
(translation_records.find_by :locale => I18n.locale).text
end
class ModelX
belongs_to :name_translation, :class_name => 'Translation'
belongs_to :description_translation, :class_name => 'Translation'
def name
name_translation.current
end
def description
description_translation.current
end
end
#Migrations
class CreateTranslationRecords < ActiveRecord::Migration[5.0]
def change
create_table :translation_records do |t|
t.references :translation
t.string :locale
t.string :text
end
add_index :translation_records, :locale
end
end
class CreateTranslation < ActiveRecord::Migration[5.0]
def change
create_table :translations do |t|
# only id column
end
end
end
class AddTranslationToModelXs < ActiveRecord::Migration[5.0]
def change
add_reference :model_xs, :name_translation
add_reference :model_xs, :description_translation
end
end

Updating Attribute Value in Rails Migration

I have over 100 recipes uploaded through ActiveAdmin (http://activeadmin.info/) in with the following attributes:
class CreateRecipes < ActiveRecord::Migration
def change
create_table :recipes do |t|
t.string :title
t.string :description
t.string :ingredients
t.string :position
t.timestamps
end
end
end
I needed to change position from a string to an integer. I was able to do this with the following:
change_column :table_name, :column_name, :integer
stackoverflow: Rails migration for change column
The issue is that I do not know how to go back and reassign all the recipes with a position (now that it's an integer). I basically want to start at 0 and go all the way to 100. And if i create a new recipe, it automatically has the position value of 101.
Is there a way to do this without going back and individually changing each recipe?
It sounds like you want to set :position to :id initially. You could do that through the rails console like so:
recipes = CreateRecipes.all
recipes.each do |recipe|
recipe.position = recipe.id
end
Then, for new recipes, in your model (create_recipes.rb), you could add:
after_initialize :default_values
...
def default_values
self.position ||= id
end
Incidentally, this is a nice clean way to handle default or initial values in general. For more information, see this excellent post How can I set default values in ActiveRecord?.
You can have the conversion run automatically as part of the migration itself. Add the code to convert the values in the existing records into the migration. Use self.up and self.down to have the appropriate conversion code for that direction of the migration:
class ChangeRecipePositionToInteger < ActiveRecord::Migration
def self.up
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :integer
position_values.each_pair do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_i
recipe.save
end
end
def self.down
position_values = Hash[ Recipe.all.map{|r| [r.id, r.position]}]
change_column :recipes, :position, :string
position_values.each_pari do |id, position_value|
recipe = Recipe.find( id )
recipe.position = position_value.to_s
recipe.save
end
end
end

Rails Globalize3 gem: How do I add an additional field to the translation table using a migration?

The docs for the Globalize3 gem are clear about how to create a translation table, but I don't see any information about how to add a field to a translation table during a later migration. For example, I initially included Category.create_translation_table! :name => :string when I created my Category model. Now, however, I need to add a translated field to the model.
How do I do that with a Rails migration? I don't see any docs for an alter_translation_table! method or anything similar...
You can do it by hand, something like the following:
class AddNewFieldToYourTable < ActiveRecord::Migration
def self.up
change_table(:your_tables) do |t|
t.string :new_field
end
change_table(:your_table_translations) do |t|
t.string :new_field
end
end
def self.down
remove_column :your_tables, :new_field
remove_column :your_table_translations, :new_field
end
end
With Globalize4, just :
class AddHintToCategory < ActiveRecord::Migration
def up
Category.add_translation_fields! hint: :text
end
def down
remove_column :category_translations, :hint
end
end
Don't forget to add the new field in your model :
translate :name, :hint
https://github.com/globalize/globalize/blob/master/lib/globalize/active_record/migration.rb:34 line (globalize 4)
add_translation_fields!(fields, options)
P.S. Just a typo in a previous comment, 'add_transaction_fields' isn't defined.

is their any date filter in ruby?

1)
i have a leave request form in my HRM application, i want that when a user enter the "from_date", the "to_date" should be greater than "from_that"?
2)
In my app i have a leave model in which user can defined leave type and day's allowed.In my second model leave_request user can request for leave according to leave type.Everything is working well.Now i want to display leave balance available to user on basis of allowed day's in leave_request form?
Migration leave.rb:
class CreateLeaves < ActiveRecord::Migration
def self.up
create_table :leaves do |t|
t.integer :user_id
t.integer :company_id
t.string :leave_type
t.integer :allowed_leaves
t.text :description, :limit => 500
t.timestamps
end
end
def self.down
drop_table :leaves
end
end
And leave_request.rb:
class CreateLeaveRequests < ActiveRecord::Migration
def self.up
create_table :leave_requests do |t|
t.integer :employee_id
t.integer :leave_type
t.date :from_date
t.date :to_date
t.text :reason_for_leave
t.string :contact_during_leave, :limit => 10
t.integer :user_id
t.integer :company_id
t.timestamps
end
end
def self.down
drop_table :leave_requests
end
end
And can i validate number of leaves available like if user do not have enough leave balance than their should be a message like"You do not have enough leave to request " etc.
Your first question is not very comprehensible. What is "from_that"? If you're asking how to check if one date is before or after another, it's easy: Date and DateTime include Comparable so you can compare any two just like you would two numbers, e.g.:
>> DateTime.now > 1.day.ago
# => true
>> DateTime.now > Date.tomorrow
# => false
You can write your own validator, something like this (untested code):
class LeaveRequest < ActiveRecord::Base
validate :has_enough_leave
def has_enough_leave
if( to_date - from_date > employee.allowed_leave.days )
errors.add(:to_date, "You do not have enough leave to requestt")
end
end
...
I have done my first problem in following way:
class LeaveRequest < ActiveRecord::Base
belongs_to :user
belongs_to :leave
validate :from_date_and_to_date
def from_date_and_to_date
if(to_date != from_date)
errors.add(:to_date, " should be greater than from date" )
end
end
Hi friends here i came with a new answer of my second question
i used following line of code to fix my problem:
<label>Leave Type</label>
<label>Balance</label>
<% for leave in #leave_types %>
<%= leave.leave_type %>
<%= leave.allowed_leaves%>
<% end %>
This displayed all my leave_types in my new leave_request form.

how (replace|create) an enum field on rails 2.0 migrations?

I would like to create an enum field at sone migration I'm doing, I tried searching in google but I can't find the way to do it in the migration
the only thing I found was
t.column :status, :enum, :limit => [:accepted, :cancelled, :pending]
but looks like the above code runs only on rails 1.xxx and since I'm running rails 2.0
this what I tried but it fails
class CreatePayments < ActiveRecord::Migration
def self.up
create_table :payments do |t|
t.string :concept
t.integer :user_id
t.text :notes
t.enum :status, :limit => [:accepted, :cancelled, :pending]
t.timestamps
end
end
def self.down
drop_table :payments
end
end
So, in case that isn't allowed, what do you think could be a good solution? just a text field, and validating from the model?
You can manually specify the type by using the t.column method instead. Rails will interpret this as a string column, and you can simply add a validator to the model like Pavel suggested:
class CreatePayments < ActiveRecord::Migration
def self.up
create_table :payments do |t|
t.string :concept
t.integer :user_id
t.text :notes
t.column :status, "ENUM('accepted', 'cancelled', 'pending')"
t.timestamps
end
end
def self.down
drop_table :payments
end
end
class Payment < ActiveRecord::Base
validates_inclusion_of :status, :in => %w(accepted cancelled pending)
end
Look at tip #3 on http://zargony.com/2008/04/28/five-tips-for-developing-rails-applications
This exactly what you need!
class User < ActiveRecord::Base
validates_inclusion_of :status, :in => [:active, :inactive]
def status
read_attribute(:status).to_sym
end
def status= (value)
write_attribute(:status, value.to_s)
end
end
HTH
You can try the (very) comprehensive jeff's enumerated_attribute gem OR go with this simple workaround:
class Person < ActiveRecord::Base
SEX = [:male, :female]
def sex
SEX[read_attribute(:sex)]
end
def sex=(value)
write_attribute(:sex, SEX.index(value))
end
end
And then declare the sex attribute as an integer:
t.integer :sex
This worked very fine for me! =D
I have dozens of these little enums, with 3-300 entries in each. I implement them as lookup tables. I don't have a model file for each one; I use some metaprogramming to generate a model for each, since each table has the same set of columns (id, name, description).
Since some of the sets had enough elements to warrant their own table, it was more consistent to move them all to tables. Just another option if you'll have more of these enums later.
EDIT: Here's how I generate the models:
ACTIVE_RECORD_ENUMS = %w{
AccountState
ClientType
Country
# ...
}
ACTIVE_RECORD_ENUMS.each do |klass|
eval "class #{klass} < ActiveRecord::Base; end"
klass.constantize.class_eval do
class << self
def id_for(name)
ids[name.to_s.strip.humanize.downcase]
end
def value_for(id)
values[id.to_i]
end
def values
#values ||= find(:all).inject({}) {|h,m| h[m.send(primary_key)] = m.name; h}
end
def ids
#ids ||= self.values.inject({}) {|h, {k, v}| h[v.downcase] = k; h}
end
end
end
end
This file lives in the models directory, and is included in application_config.rb. This lets me do stuff like this:
AccountState.ids
# => {"active" => 1, "deleted" => 2}
AccountState.values
# => {1 => "Active", 2 => "Deleted"}
AccountState.id_for("Active")
# => 1
AccountState.value_for(1)
# => "active"
Have you looked at the enum-column plugin on RubyForge?
Similarly, the enumerated_attribute gem manages enums at the object level.
enum_attr :status, %w(accepted cancelled ^pending)
Define a string in the migration
t.string :status
Also provides some nice features like dynamic predicate methods.
http://github.com/jeffp/enumerated_attribute/tree/master
Add the following:
module ActiveRecord
module ConnectionAdapters #:nodoc:
class TableDefinition
def enum(*args)
options = args.extract_options!
column_names = args
column_names.each { |name| column(name, 'enum', options) }
end
end
end
end
to lib/enum/table_definition.rb and include it in your init.rb.
ok, just read the whole rails api and found what I neeed and I dont like :(
Rails doesn't support emum as native type on migrations, here is the info, I need to search for a plugin or other method.
I will keep you posted.
Another option: drop to SQL.
def self.up
execute "ALTER TABLE `payments` ADD `status` ENUM('accepted', 'cancelled', 'pending')"
end
With simple_form i use this:
<%= f.input :gender, :collection => {'Male' => 'male','Female' => 'female'}, :include_blank => false %>

Resources