How to create a migration file dynamically from library in rails 4.0 - ruby

How to create a migration file dynamically in rails 4.0?
I want to add some columns to different tables dynamically via my library module. There would be a method that create a migration file and add content to it.
How can I create it from a library?

Yes, I found a way to adding a migration file from lib. I created a method for this. The following code snippet shows the methods we use and gives a better idea to create migration file dynamically.
def create_columns(tb_with_cols)
add_columns = ""
tb_name = tb_with_cols.keys.first
columns = tb_with_cols.values.first
columns.each { |c_name, c_type| add_columns << "\tadd_column(':#{tb_name}', :#{c_name}, :#{c_type})\n" }
add_columns
end
def migration_file_content(tb_with_cols)
cols = create_columns(tb_with_cols)
<<-RUBY
class AddMissingColumnsToTable < ActiveRecord::Migration
def change_table
#{cols}
end
end
RUBY
end
def write_content_to_file(path, content)
File.open(path, 'w+') do |f|
f.write(content)
end
end
Just call the method migration_file_content in your code. Pass the parameter tb_with_cols as a Hash, in which the key is the table_name and value is the columns that should be added to that table. Ex:
tb_with_cols = {:users => {:name => :string, :age => :integer, :address => :text} }
content = migration_file_content(tb_with_cols)
write_content_to_file("#{Rails.root}/db/migrations/', content)
After that call the method write_content_to_file with your new migration file path and the content from our migration_file_content method.

Related

bad char after creating a Database from csv

I am trying to create a database using mongoid but it fails to find the create method. I am trying to create 2 databases based on csv files:
extract_data class:
class ExtractData
include Mongoid::Document
include Mongoid::Timestamps
def self.create_all_databases
#cbsa2msa = DbForCsv.import!('./share/private/csv/cbsa_to_msa.csv')
#zip2cbsa = DbForCsv.import!('./share/private/csv/zip_to_cbsa.csv')
end
def self.show_all_database
ap #cbsa2msa.all.to_a
ap #zip2cbsa.all.to_a
end
end
the class DbForCSV works as below:
class DbForCsv
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Attributes::Dynamic
def self.import!(file_path)
columns = []
instances = []
CSV.foreach(file_path, encoding: 'iso-8859-1:UTF-8') do |row|
if columns.empty?
# We dont want attributes with whitespaces
columns = row.collect { |c| c.downcase.gsub(' ', '_') }
next
end
instances << create!(build_attributes(row, columns))
end
instances
end
private
def self.build_attributes(row, columns)
attrs = {}
columns.each_with_index do |column, index|
attrs[column] = row[index]
end
ap attrs
attrs
end
end
I am not aware of all fields and it may change in time. that's why I have create database and generic mehtods.
I have also another issue after having fixed the 'create!' issue.
I am using the encoding to make sure only UTF8 char are handled but I still see:
{
"zip" => "71964",
"cbsa" => "31680",
"res_ratio" => "0.086511098",
"bus_ratio" => "0.012048193",
"oth_ratio" => "0.000000000",
"tot_ratio" => "0.082435345"
}
when doing 'ap attrs' in the code. how to make sure that 'zip' -> 'zip'
Thanks
create! is a class method but you're trying to call it as an instance method. Your import! method shouldn't be an instance method either, it should be a class method since it produces instances of your class:
def self.import!(file_path)
#-^^^^
# everything else would be the same...
end
You'd also make build_attributes a class method since it is just a helper method for another class method:
def self.build_attributes
#...
end
And then you don't need that odd looking new call when using import!:
def self.create_all_databases
#cbsa2msa = DbForCsv.import!('./share/private/csv/cbsa_to_msa.csv')
#zip2cbsa = DbForCsv.import!('./share/private/csv/zip_to_cbsa.csv')
end

What methods are used to set attributes when data is pulled from a table in Ruby ActiveRecord?

I'm using ActiveRecord 4.1.8 in a Ruby (not Rails) application. I have a table and a corresponding model that looks like the following:
create_table 'people', :force => true do |t|
t.string 'name'
end
class Person < ActiveRecord::Base
def name=(name)
puts "Attribute setter for name called with #{name}"
write_attribute(:name, name)
end
end
When I create a new instance of Person, I see the Attribute setter for name called with... written to STDOUT. However, when I reload the model instance, I do not see the message written to STDOUT.
p = Person.create(name: 'foobar')
--> Attribute setter for name called with foobar
p.reload
--> <nothing>
The model is getting persisted to the database, so this makes me think name= isn't used when data is loaded into a model from the database. I need to modify certain data attributes when they're read in from the database, so does anyone know what other method I need to override?
From the active_record/persistence.rb source:
def reload(options = nil)
clear_aggregation_cache
clear_association_cache
fresh_object =
if options && options[:lock]
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
else
self.class.unscoped { self.class.find(id) }
end
#attributes = fresh_object.instance_variable_get('#attributes')
#new_record = false
self
end
It just replaces the attributes hash directly. Seems like the easiest way to handle this is to override reload and patch things up after its called.

Ruby not allowing dynamic strings as an argument

I have a class already mapped out and in a database through DataMapper and now I'm trying to make my first resource into the database.
I have a class that handles the form data and file stuff. In that class, I'm creating the first resource with #variables passed in from the params. All other args passed into this resource come from #variables that have values from the form. In this case, #url, the variable in question, is set to a value only a few lines before. Now when I put in the URL:
rec = Post.new(
# more args
:filename_ogg => #url
)
rec.save
This is the killer: Every other line of code in this file is able to access #url, through a global variable ($upload = Upload.new(file)), except for this resource creator. When it comes to saving the resource, it doesn't go through. BUT, when I replace #url with a static string like "RANDOM URL.", it works perfectly. Why?
This had been tested under both MRI 1.9.3 and JRuby 1.6.7.2 (1.9 mode) under Ubuntu 12.04:
# #{user} edited out
class Upload
attr_accessor :file, :filename, :filename_ogg, :status, :title, :desc, :url
def initialize(file)
#file = file
#filename = #file[:filename].gsub(" ", "")
#filename_ogg = "#{#filename}.ogg"
##url = "http://s3.amazonaws.com/#{user}/#{#filename_ogg}"
end
def downandup
# code
end
def convert(file, file_ogg)
# code
end
def upload(file_ogg)
# code
#url = "http://s3.amazonaws.com/#{user}/#{file_ogg}"
# title and desc are accessed through $upload.title/$upload.desc
rec = Post.new(
:title => #title,
:description => #desc,
:author_id => Random.rand(5),
:time_uploaded => Time.now,
:filename_ogg => #url,
:comments_table => Random.rand(10),
)
rec.save
end
end
The file runs through fine, but when it comes for DataMapper to put it in the database, it won't go in, but when replaced with the static string, the data gets stored.

How do you handle serialized edit fields in an Active Admin resource?

I have a model, Domain, which has a text field, names.
> rails g model Domain names:text
invoke active_record
create db/migrate/20111117233221_create_domains.rb
create app/models/domain.rb
> rake db:migrate
== CreateDomains: migrating ==================================================
-- create_table(:domains)
-> 0.0015s
== CreateDomains: migrated (0.0066s) =========================================
I set this field as serialized into an array in the model.
# app/models/domain.rb
class Domain < ActiveRecord::Base
serialize :names, Array
end
Create the ActiveAdmin resource for this model
> rails g active_admin:resource Domain
create app/admin/domains.rb
then, in the app/admin/domains.rb, I setup the various blocks to handle the serialized field as such
# app/admin/domains.rb
ActiveAdmin.register Domain do
index do
id_column
column :names do |domain|
"#{domain.names.join( ", " ) unless domain.names.nil?}"
end
default_actions
end
show do |domain|
attributes_table do
row :names do
"#{domain.names.join( ", " ) unless domain.names.nil?}"
end
end
end
form do |f|
f.inputs "Domain" do
f.input :names
end
f.buttons
end
# before we save, take the param[:domain][:name] parameter,
# split and save it to our array
before_save do |domain|
domain.names = params[:domain][:names].split(",") unless params[:domain].nil? or params[:domain][:names].nil?
end
end
Nearly everything works great -- my names are displayed as comma separated in the index and show views. When I update a record with my names field set to "a,b,c", the before_save works to turn that into an array that is then saved via the ActiveRecord serialize.
What I can not solve is how to make the edit form put in a comma-separated list into the text field. I tried using a partial and using formtastic syntax directly as well as trying to make it work via the active_admin DLS syntax. Does anyone know how to make this work?
Specifically, if I have the following array saved in my domain.names field:
# array of names saved in the domain active_record
domain.names = ["a", "b", "c"]
how to change:
form do |f|
f.inputs "Domain" do
f.input :names
end
f.buttons
end
so that when the edit form is loaded, in the text field instead of seeing abc, you see a,b,c.
Here is a summary of how I handled this situation. I added an accessor to the model which can turn the Array into a string joined by a linefeed and split it back to an Array.
# app/models/domain.rb
class Domain < ActiveRecord::Base
serialize :names, Array
attr_accessor :names_raw
def names_raw
self.names.join("\n") unless self.names.nil?
end
def names_raw=(values)
self.names = []
self.names=values.split("\n")
end
end
then, in my admin resource for domain, instead of using the :names field, I used the :names_raw field. setting this value would save the names Array with the new values.
# app/admin/domains.rb
form do |f|
f.inputs "Domain" do
f.input :names_raw, :as => :text
end
f.buttons
end
Stumbled on this question looking for something to have access to a serialized Hash's YAML. I used this solution on Rails 3.2:
def target_raw
#attributes['target'].serialized_value
end
def target_raw=(new_value)
#attributes['target'].state = :serialized
#attributes['target'].value = new_value
end

Active Record to_json\as_json on Array of Models

First off, I am not using Rails. I am using Sinatra for this project with Active Record.
I want to be able to override either to_json or as_json on my Model class and have it define some 'default' options. For example I have the following:
class Vendor < ActiveRecord::Base
def to_json(options = {})
if options.empty?
super :only => [:id, :name]
else
super options
end
end
end
where Vendor has more attributes than just id and name. In my route I have something like the following:
#vendors = Vendor.where({})
#vendors.to_json
Here #vendors is an Array vendor objects (obviously). The returned json is, however, not invoking my to_json method and is returning all of the models attributes.
I don't really have the option of modifying the route because I am actually using a modified sinatra-rest gem (http://github.com/mikeycgto/sinatra-rest).
Any ideas on how to achieve this functionality? I could do something like the following in my sinatra-rest gem but this seems silly:
#PLURAL.collect! { |obj| obj.to_json }
Try overriding serializable_hash intead:
def serializable_hash(options = nil)
{ :id => id, :name => name }
end
More information here.
If you override as_json instead of to_json, each element in the array will format with as_json before the array is converted to JSON
I'm using the following to only expose only accessible attributes:
def as_json(options = {})
options[:only] ||= self.class.accessible_attributes.to_a
super(options)
end

Resources