Creating an ActiveRecord database for a webcrawler. Having issues adding index - ruby

I'm working on a three part program for a webcrawler program for school. My first step is to just create the database using activerecord and knowing that it will be queried later using sqlite3. The database actually does get created with my current program. However, it errors out when gets to "add_index". I'm not great with databases in general so guidance is appreciated.
Before I put in my code the error is:
crawler_create.rb:23: syntax error, unexpected ',', expecting ')'
add_index (:pages,[:url], unique=> true)
Here's what I've come up with so far:
require 'active_record'
require 'sqlite3'
db=SQLite3::Database.new("crawler.db")
ActiveRecord::Base.establish_connection(adapter:'sqlite3',database:'crawler.db')
ActiveRecord::Schema.define do
create_table :pages do |t|
t.string :url, :null => false
t.string :title
t.string :content_type
t.date :last_modified
t.string :status
end
end
add_index (:pages,[:url], unique=> true)
ActiveRecord::Schema.define do
create_table :links do |t|
t.integer :from_page_id, :null => false
t.integer :to_page_id, :null => false
end
end
add_index(:links,[:from_page_id,:pages_id], :unique =>true)
ActiveRecord::Schema.define do
add_foreign_key :from_page_id, :pages_id
add_foreign_key :to_page_id,:pages_id
end

It looks like you got a simple syntax error here:
add_index (:pages, [:url], unique: true)
I'd suggest that you use a consistent hash syntax, either using hash rockets => or the newer syntax unique: true.

Related

Rake Setup - ActiveRecord::RecordInvalid: Validation failed:

In trying to run a setup rake tasks(to populate my dbase). It is telling me that questions must exist. When I do a count on 'Question.count' - it returns 4 - so I know questions exist.
What am I missing? typo? syntax? When I look at similiar SO, it shows as some sort of typo but I am not seeing it.
Here is the code that is not working:
def add_option_group
puts " * Add Option Groups...\n"
OptionGroup.transaction do
create_option_group(
option_group_name: "Always-Never",
question_id: Question.find_by(question_name: "Do you have an updated photo?")
)
create_option_group(
option_group_name: "Yes-No",
question_id: Question.find_by(question_name: "Do you have a bio saved (updated in last 12 months)?")
)
end
end
def create_option_group(options={ })
puts " * CREATE OptionGroup Section...\n"
option_group_attributes = {}
attributes = option_group_attributes.merge options
option_group = OptionGroup.create! attributes
option_group.save!
option_group
end
I am getting this error message:
Add Option Groups...
CREATE OptionGroup Section...
rake aborted!
ActiveRecord::RecordInvalid: Validation failed: Questions must exist
/Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:314:in create_option_group' /Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:298:in block in add_option_group'
/Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:297:in add_option_group' /Users/axxx/workspace/fresh-assess/lib/tasks/setup.rake:51:in create_sample_data!
Migration files:
***** migrations ******
class CreateOptionGroups < ActiveRecord::Migration[6.1]
def change
create_table :option_groups do |t|
t.bigint :question_id
t.text :option_group_name
t.timestamps
end
end
end
class CreateQuestions < ActiveRecord::Migration[6.1]
def change
create_table :questions do |t|
t.bigint :assessment_section_id
t.bigint :input_type_id
t.text :question_name
t.string :question_subtext
t.boolean :question_required_yn
t.boolean :answer_required_yn
t.boolean :allow_multiple_options_answers_yn
t.integer :dependent_question_id
t.integer :dependent_question_option_id
t.integer :dependent_answer_id
t.timestamps
end
end
end
Here is schema
t.bigint "question_id"
t.bigint "option_choice_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "questions", force: :cascade do |t|
t.bigint "assessment_section_id"
t.bigint "input_type_id"
t.text "question_name"
t.string "question_subtext"
t.boolean "question_required_yn"
t.boolean "answer_required_yn"
t.boolean "allow_multiple_options_answers_yn"
t.integer "dependent_question_id"
t.integer "dependent_question_option_id"
t.integer "dependent_answer_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Add foreign key:
class ForeignMoreKeysToModels < ActiveRecord::Migration[6.1]
def change
add_foreign_key :option_groups, :questions, validate: false
add_foreign_key :option_choices, :option_groups, validate: false
end
end
In the model:
class Question < ApplicationRecord
has_one :assessment_section
belongs_to :assessment_section, optional: true
has_many :option_groups
end
class OptionGroup < ApplicationRecord
has_many :option_choices
belongs_to :questions
end
What am I missing?
thx.
Two issues:
In the OptionGroup class, it should say belongs_to :question.
Try adding .id to the question lookup:
create_option_group(
option_group_name: "Always-Never",
question_id: Question.find_by(question_name: "Do you have an updated photo?").id
)

How do I properly add a foreign key in active record?

I'm trying to create some referential integrity across several tables, and am tripping over the placement of add foreign keys. At best the statement is ignored, worst it throws an error.
class CreateCantons < ActiveRecord::Migration
def change
create_table :cantons do |t|
t.integer :canton_id
t.string :canton_name
t.timestamps null: false
end
end
end
class CreateResources < ActiveRecord::Migration
def change
create_table :resources do |t|
t.integer :resource_id
t.string :resource_name
t.integer :type_id
t.integer :canton_id
t.string :url
t.string :address
t.string :city
t.string :state
t.string :zip
add_foreign_key :cantons, :canton_id #ignored
add_foreign_key :types, :type_id #ignored
t.timestamps null: false
end
end
end
class CreateResourceContacts < ActiveRecord::Migration
def change
create_table :resource_contacts do |t|
t.integer :contact_id
t.integer :resource_id
add_foreign_key :resources, :resource_id
add_foreign_key :contacts, :contact_id
t.timestamps null: false
end
end
end
adding a t in front throws an error
t.add_foreign_key :contacts, :contact_id #error
How do I properly use this command?
you need to move the foreign_keys outside of the create table
class CreateResources < ActiveRecord::Migration
def change
create_table :resources do |t|
t.integer :resource_id
t.string :resource_name
t.integer :type_id
t.integer :canton_id
t.string :url
t.string :address
t.string :city
t.string :state
t.string :zip
t.timestamps null: false
end
add_foreign_key :resources, :cantons
add_foreign_key :resources, :types
end
end
see http://edgeapi.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key
And also you need to tell it what tables you are adding it to.

SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer/

I ran the command
rails g migration AddUser_idToComments User_id:string
and then I figured out that User_id should be an integer and so I ran
rails g migration AddUser_idToComments User_id:integer --force thinking that it would overwrite the initial command.
But now, I'm getting this error:
```
louismorin$ rake db:migrate
== 20140910155248 AddIndexToComments: migrating ===============================
-- add_column(:comments, :Index, :string)
-> 0.0069s
== 20140910155248 AddIndexToComments: migrated (0.0070s) ======================
== 20140910181022 AddUserIdToComments: migrating ==============================
-- add_column(:comments, :User_id, :integer)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer/Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:in change'
ActiveRecord::StatementInvalid: SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer
/Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:inchange'
SQLite3::SQLException: duplicate column name: User_id
/Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:in `change'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
```
Here's my schema.rb file
```
ActiveRecord::Schema.define(version: 20140910155210) do
create_table "comments", force: true do |t|
t.text "body"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "User_Id"
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id"
create_table "posts", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "topic_id"
end
add_index "posts", ["topic_id"], name: "index_posts_on_topic_id"
add_index "posts", ["user_id"], name: "index_posts_on_user_id"
create_table "topics", force: true do |t|
t.string "name"
t.boolean "public", default: true
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "role"
t.string "avatar"
t.string "Image"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
```
Because of the error, your second migration -- changing the column type -- didn't run. If that migration was only intended to change that one column, we could then delete the file it generated and try again.
If you don't yet have any data in that column that you care about, that's pretty easy:
rails g migration ChangeTypeOfUserIdOnComments
This migration name isn't special. Might as well have been DoWhateverIWant
Then, edit the created migration's change method to something like this:
def change
remove_column :comments, :user_id
add_column :comments, :user_id, :integer
add_index :comments, :user_id
end
When you then run your un-run migrations rake db:migrate, it shouldn't trip up on the one which errored (because we deleted it), and then it should run this one, which removes the column and adds it back with the correct type.
If you DO have data you want to save, the procedure is more complicated. Your change method would have to grab the current user_id's for each comment, and then assign them to the new comments when we create the new column. The below should hopefully work:
def change
user_ids = {}
Comment.find_each{ |c| user_ids[c.id] = c.user_id.to_i }
remove_column :comments, :user_id
add_column :comments, :user_id, :integer
add_index :comments, :user_id
Comment.each{ |c| c.update_attribute(:user_id, user_ids[c.id])
end
Also note that the names of the migration in the command are generally all CamelCase or all snake_case. So: AddColumnUserIdToComments or add_column_user_id_to_comments. Naming as you did might cause problems.
** EDIT **
Modify a Column's Type in sqlite3
It appears that SQLite has NO good way to modify/drop a column. I'd suggest either:
Dropping and restarting a SQLITe table
Drop the table, drop the original user_id migration and the line in the new one about removing the old user_id column, and then create a new table with the new migrations. Should work fine if you don't care about your data
Switching to Postgres, which is the database Heroku uses.
It's probably a good idea (because you want your production and local databases to behave identically), but can be bug-prone to switch to.
Look here for guidance -- Change from SQLite to PostgreSQL in a fresh Rails project

rails joins with module name

Here is how to join two models
User.where(:id => 1).joins(:posts)
but how to join two models with module/namspace
#schedules= Swimming::Classschedule.joins(:Swimming::Slot).where(:date => #date)
seems not working properly (with error message)
:Swimming is not a class/module
UPDATE
I have updated to
#schedules= Swimming::Classschedule.joins(:swimming_slots).where(:date => #date)
and I do have this table
create_table :swimming_classschedules do |t|
t.integer :slot_id
t.integer :coach_id
t.integer :level_id
t.string :note
t.timestamps
end
create_table :swimming_slots do |t|
t.string :date
t.string :start
t.string :end
t.timestamps
end
Howcome I got this error
Association named 'swimming_slots' was not found; perhaps you misspelled it?
update 2
add this line to Swimming::Classschedule module
belongs_to :swimming_slots ,:class_name=>'Swimming::Slot',:foreign_key => "slot_id"
and
change joins to
#schedules= Swimming::Classschedule.joins(:swimming_slots).where(:swimming_slots =>{:date => #date})
Now it works
you pass the association name to joins. for example, if you have an association like
has_many :swimming_slots, class_name: 'Swimming::Classschedule'
then you pass swimming_slots and rails will do the joins for you.
User.joins(:swimming_slots)
UPDATE:
if slot_id refers to a record in the swimming_slots table, you should have something like
belongs_to :slot, class_name: 'Swimming::Slot'
in your class schedule model. If you have that, you should be able to do
Swimming::Classschedule.joins(:slot)

Activerecord Rails 3 string limits not working

I have a rails migration and I am most probably doing something incorrect here but
the migration is ---
class CreateStates < ActiveRecord::Migration
def change
create_table :states do |t|
t.string :state_legacy_id
t.string :name, :length => 20
t.string :abbreviation, :length => 2
t.timestamps
end
add_index :states, :id
end
end
But when i go into mysql database and look at the table the name table as well as the abbreviation table have a length of varchar/ 255 What is about my limit statement that is incorrect.
I have tried both with and without quotes, so :limit => 20 and :limit => "20" both product tables with varchar 255.
Any suggestions will be welcome.
Thanks,
i believe it's called limit - not length
e.g. :limit => 2

Resources