Bug with ActiveRecords - ruby

I can't delete information about tables and columns from ActiveRecord's cache.
I'm using ActiveRecord for Ruby without Rails.
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "mysql2",
:database => #
:password => #
)
class Person < ActiveRecord::Base
belongs_to :peoples
end
enter code here
class Persons < ActiveRecord::Base
belongs_to :peoples
end
class People < ActiveRecord::Base
has_many :persons
end
Person.new
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'vkusno.people' doesn't exist: SHOW FULL FIELDS FROM `people`'
Persons.new
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'vkusno.persons' doesn't exist: SHOW FULL FIELDS FROM `persons`'
But I try to connect to a Database without some table and columns.
Before I had a table in the database, "People, Peoples, Person, Persons", but I drop all my tables and restarted my server a few times.
If I change my database to sqlite, I get some don't exist tables, which it I'm working and drop that tables too.
How I can repair it?
UPD
ActiveRecord::Base.logger = Logger.new(STDOUT)
class AddFirst < ActiveRecord::Migration
def up
create_table :persons do |t|
t.integer :idd
t.string :name
t.string :href
t.string :sex
t.string :country
t.string :city
t.boolean :can_message
t.boolean :can_wall
t.string :photo
t.boolean :is_friend
t.boolean :is_client
end
create_table :people do |x|
x.integer :id_general
x.string :description
end
end
def down
drop_table :persons
drop_table :people
end
def keys
add_column :persons, :peoples_id, :integer
add_index :persons, :peoples_id
end
end
> AddFirst.new.up
-- create_table(:persons)
CREATE TABLE `persons` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `idd` int(11), `name` varchar(255), `href` varchar(255), `sex` varchar(255), `country` varchar(255), `city` varchar(255), `can_message` tinyint(1), `can_wall` tinyint(1), `photo` varchar(255), `is_friend` tinyint(1), `is_client` tinyint(1)) ENGINE=InnoDB
-- create_table(:people)
CREATE TABLE `people` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `id_general` int(11), `description` varchar(255)) ENGINE=InnoDB
=> {}
AddFirst.new.keys
-- add_column(:persons, :peoples_id, :integer)
ALTER TABLE `persons` ADD `peoples_id` int(11)
-- add_index(:persons, :peoples_id)
CREATE INDEX `index_persons_on_peoples_id` ON `persons` (`peoples_id`)
=> nil
> Person.new
=> #<Person id: nil, id_general: nil, description: nil>
> Persons.new
=> #<Persons id: nil, idd: nil, name: nil, href: nil, sex: nil, country: nil, city: nil, can_message: nil, can_wall: nil, photo: nil, is_friend: nil, is_client: nil>
> People.new
=> #<People id: nil, id_general: nil, description: nil>

Understanding how class names are mapped to table names
You need to understand how ActiveRecord decides the table name for a model.
If you have a class Person, the default table name will be people. AR does this by calling the tableize method on the class name, like this:
'Person'.tableize # => "people"
or
Person.name.tableize # => "people"
That means, for the Person class, you should create the people table (unless you want to override the default). Make sure you get the spelling correct; peoples will not work.
That also means you should not have another class People because the default table name will also be people:
'People'.tableize #=> "people"
There will be a clash, or at best, you'll confuse yourself.
It's not a good idea to use persons as the table name, unless you're overriding the default. That's because AR will not generate persons as the table name for class Person.
I strongly encourage you to read and understand the ActiveRecord basics guide before you try anything else.
Modeling the real world
I'm also not sure why you want Person to belong to :peoples.
Can you describe the real-world scenario you're trying to represent?
Examples:
a person has many books
a person belongs to an organization.

Related

create unique constraints per user

I am building a small app and at this point I am creating the database schema. I use PostgreSQL with Sequel and I have the two migrations:
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :user do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
DateTime :created_at
DateTime :updated_at
index :id, :unique => true
end
end
end
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :location do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
foreign_key :user_id, :user, :type => :uuid
String :name, :unique => true, :null => false # TODO: unique, per user
Float :latitude, :null => false
Float :longitude, :null => false
DateTime :created_at
DateTime :updated_at
index :id, :unique => true
full_text_index :name, :index_type => :gist
end
end
end
As you can see the name column on the location table is unique, but I am in doubt about it. Because I want to establish a unique constraint on the name, but per user, so a user can have only one location with the same name but in the entire table many users can have locations with the same name (the relation between user and location is a one to many).
I suppose my unique constraint added on the column will make column generally unique, so amongst all users the location name must be unique. If so, how do I create a constraint that makes the name unique on a per user basis?
Just create the unique constraint over both columns:
UNIQUE (user_id, name)
Per documentation:
This specifies that the combination of values in the indicated columns
is unique across the whole table, though any one of the columns need
not be (and ordinarily isn't) unique.
But from the looks of it, you really want another table user_location than implements an n:m relation between locations and users - with a primary key on (user_id, location_id).
And don't call the first table "user", that's a reserved word in standard SQL and in Postgres and shouldn't be used as identifier.

create unique constraints per user in tables with n:m relation

I have asked a question a few moments ago, create unique constraints per user and the answer was very simple.
For creating a unique index on a column, but on a per user basis, all I have to do is:
unique [:user_id, :name] # SQL syntax: UNIQUE (user_id, name)
But the relation between the user table and the table that references the user_id is a 1:n (user to location), so I have a foreign_key inside the location table which references user_id. This question is about a n:m relation between two tables and adding a unique constraint on one of the tables.
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :customer do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
DateTime :created_at
DateTime :updated_at
index :id, :unique => true
end
end
end
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :role do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
String :name, :unique => true, :null => false # TODO: unique, per customer
DateTime :created_at
DateTime :updated_at
unique [:customer_id, :name]
index :id, :unique => true
full_text_index :name, :index_type => :gist
end
end
end
The above code illustrates the two tables I mentioned have a n:m relation (customer to role), the following table illustrates the join table with the foreign keys for both the tables:
Sequel.migration do
change do
Sequel::Model.db.run 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'
create_table :customer_role do
String :id, :type => :uuid, :primary_key => true, :default => Sequel.function(:uuid_generate_v4)
foreign_key :customer_id, :customer, :type => :uuid
foreign_key :role_id, :role, :type => :uuid
index :id, :unique => true
end
end
end
What I would like to do is to declare a unique constraint such as UNIQUE (customer_id, :name) on the role table. But I cannot do that, so how do I achieve that in another way?
What I would like to do is to declare a unique constraint such as
UNIQUE (customer_id, :name) on the role table. But I cannot do that,
so how do I achieve that in another way?
The essence of your problem seems to be that the column role.customer_id doesn't exist. In fact, I'm pretty sure that column shouldn't exist, so that part's actually good.
At the very least, you need
unique [:customer_id, :role_id]
in "customer_role".

SQLite Creating Table Dynamically

I have a table in SQLite and when an entry is made in this table, I would like to create a new table for each entry in the first table. (I want to do this in Ruby On Rails, if that helps)
Here is an example to clarify what I am trying to achieve:
Assume there is a table: Campaigns
Campaigns
Campaign_ID, date, name
So if I make an entry:
01 06/12 FirstCampaign
is there a way to create a new table called: {Campaign_ID}_Page
i.e:
01_Page
(fields for this table go here)
Why do you need it?
I think it would be better to create a table Pages with a foreign key to your table Campaigns.
An example (I use Sequel):
require 'sequel'
DB = Sequel.sqlite
DB.create_table :Campaigns do
primary_key :id
column :campaign_id, :integer
column :date, :date
column :name, :string
end
DB.create_table :Pages do
primary_key :id
foreign_key :campaign_id, :Campaigns
column :text, :string
end
key = DB[:Campaigns].insert(:campaign_id => 01, :date=> Date.new(2012,1,1), :name => 'FirstCampaign')
DB[:Pages].insert(:campaign_id => key, :text => 'text for FirstCampaign')
key = DB[:Campaigns].insert(:campaign_id => 02, :date=> Date.new(2012,1,1), :name => 'SecondCampaign')
DB[:Pages].insert(:campaign_id => key, :text => 'text for SecndCampaign')
#All pages for 1st campaign
p DB[:Pages].filter(
:campaign_id => DB[:Campaigns].filter(:campaign_id => 1).first[:id]
).all
But to answer your question: You could try to use a model hook.
An example with Sequel:
require 'sequel'
DB = Sequel.sqlite
DB.create_table :Campaigns do
primary_key :id
column :campaign_id, :integer
column :date, :date
column :name, :string
end
class Campaign < Sequel::Model
def after_create
tabname = ("%05i_page" % self.campaign_id).to_sym
puts "Create table #{tabname}"
self.db.create_table( tabname ) do
foreign_key :campaign
end
end
end
p DB.table_exists?(:'01_page') #-> false, table does not exist
Campaign.create(:campaign_id => 01, :date=> Date.new(2012,1,1), :name => 'FirstCampaign')
p DB.table_exists?(:'00001_page') #-> true Table created
My example has no test, if the table already exist. If you really want to use it,

Ror Active Record "component" classes

coming from my experience with nHibernate, I wonder how one maps (n)hibernate's component classes in ActiveRecord
class Person < ActiveRecord::Base
#somehow get a :name attribute here
end
class Name
#fist_name
#last_name
end
how can this be done with one table only (so this is no 1:1, but i want to have a :name_first_name column in the db (or whatever the convention is)?
That's what composed_of is for.
For You example:
class Person < ActiveRecord::Base
composed_of :name, :class_name => "Name", :mapping =>
[ # database ruby
%w[ first_name first_name ],
%w[ last_name last_name ]
],
end
class Name
attr_accessor :first_name, :last_name
end
You'll then have to add two database columns ( first_name, last_name ).

ActiveRecord/sqlite3 column type lost in table view?

I have the following ActiveRecord testcase that mimics my problem. I have a People table with one attribute being a date. I create a view over that table adding one column which is just that date plus 20 minutes:
#!/usr/bin/env ruby
%w|pp rubygems active_record irb active_support date|.each {|lib| require lib}
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => "test.db"
)
ActiveRecord::Schema.define do
create_table :people, :force => true do |t|
t.column :name, :string
t.column :born_at, :datetime
end
execute "create view clowns as select p.name, p.born_at, datetime(p.born_at, '+' || '20' || ' minutes') as twenty_after_born_at from people p;"
end
class Person < ActiveRecord::Base
validates_presence_of :name
end
class Clown < ActiveRecord::Base
end
Person.create(:name => "John", :born_at => DateTime.now)
pp Person.all.first.born_at.class
pp Clown.all.first.born_at.class
pp Clown.all.first.twenty_after_born_at.class
The problem is, the output is
Time
Time
String
When I expect the new datetime attribute of the view to be also a Time or DateTime in the ruby world. Any ideas?
I also tried:
create view clowns as select p.name, p.born_at, CAST(datetime(p.born_at, '+' || '20' || ' minutes') as datetime) as twenty_after_born_at from people p;
With the same result.
Well, after more investigation, I found that:
MySQL works:
%w|pp rubygems active_record irb active_support date|.each {|lib| require lib}
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:username => "root",
:database => "test2"
)
ActiveRecord::Schema.define do
create_table :people, :force => true do |t|
t.column :name, :string
t.column :born_at, :datetime
end
execute "create view clowns as select p.name, p.born_at, (p.born_at + INTERVAL 20 MINUTE) as twenty_after_born_at from people p;"
end
class Person < ActiveRecord::Base
validates_presence_of :name
end
class Clown < ActiveRecord::Base
end
Person.create(:name => "John", :born_at => DateTime.now)
pp Person.all.first.born_at.class
pp Clown.all.first.born_at.class
pp Clown.all.first.twenty_after_born_at.class
Produces:
Time
Time
Time
Reading the sqlite3 adapter source code, I found out that it uses PRAGMA table_info(table_name) to get the type information, and that does not return the types for views:
sqlite> pragma table_info('people');
0|id|INTEGER|1||1
1|name|varchar(255)|0||0
2|born_at|datetime|0||0
sqlite> pragma table_info('clowns');
0|name|varchar(255)|0||0
1|born_at|datetime|0||0
2|twenty_after_born_at||0||0
Therefore it may be a limitation of the adapter or just a sqlite3's views limitation. I have opened a ticket for ActiveRecord:
Also, quoting this mail in sqlite-users:
RoR should be using the
sqlite3_column_type() API to determine
the type of the values returned from a
query. Other APIs like
sqlite3_column_decltype() and pragma
table_info are returning other
information, not the type of the
result value.
Well, basically there is no datatime type in SQLite as opposed to MySQL. In your example you explicitly define types for the table but do not specify types for the view. That might be the problem. Can not check it since I have never touched ruby.

Resources