Friendly with existing Rails 4 app: No change - ruby

I have an existing app with devise running. User and log in and out etc. Thought I would like to get localhost:3000/users/15 changed to localhost:3000/users/ruby-boy so I have installed https://github.com/norman/friendly_id
I have ran rails generate friendly_id but when I ran rails generate scaffold user name:string slug:string:uniq, it say I already got a user.rb file so ok I've added: rails g migration AddSlugToUser slug:string:uniq and rails g migration AddNameToUser name:string. So I've added those two columns to my users table.
In my controllers I've replaced:
User.find(params[:id])
with:
User.friendly.find(params[:id])
Then ran this in console (rails c)
User.find_each(&:save)
But User.find_each(&:save) gave:
(0.1ms) commit transaction
(0.0ms) begin transaction
=> nil
I think I've followed the docs "eye for eye". Have I missed anything as the links wont change.
PS: I have edited the user.rb per website on github.
My user.rb:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :slugged
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :plan
has_one :profile
attr_accessor :stripe_card_token
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
end
end
Schema for users table:
create_table "users", force: :cascade 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.datetime "created_at"
t.datetime "updated_at"
t.integer "plan_id"
t.string "stripe_customer_token"
t.string "slug"
t.string "name"
end

Add :name to your sign up form and then validate for uniqueness in your User model
Class User
validates :name, uniqueness: true
This way same name can't be created more than once and you'll avoid avoid weird slugs.

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
)

Rails ActiveRecord relationships, STI, and inheritance

OK, so it's been years since I've written any ruby code, and my design may be incorrect. With that in mind, I'm writing a small utility to clone project entities in TargetProcess via REST. Target Process has a data model that allows for several types of parent:child relationships:
project:epic:feature:user_story
project:feature:user_story
project:user_story
However, all the entities are nearly identical from a data structure perspective, so it seemed to make sense to use STI and use models to define the relationships and inheritance. I've created a new Rails app with only these models to verify the error I'm getting when I attempt to associate an Epic with a Feature:
ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id`
Here are the models:
class TargetProcessEntity < ActiveRecord::Base
end
class Project < TargetProcessEntity
has_many :epics
has_many :features
has_many :user_stories
end
class Project < TargetProcessEntity
has_many :epics
has_many :features
end
class Epic < TargetProcessEntity
belongs_to :project
has_many :features
end
class Feature < TargetProcessEntity
belongs_to :project
belongs_to :epic
has_many :user_stories
end
class UserStory < TargetProcessEntity
belongs_to :feature
belongs_to :project
end
Here is the schema:
ActiveRecord::Schema.define(version: 20150929122254) do
create_table "epics", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "target_process_entity_id"
t.integer "project_id"
end
add_index "epics", ["project_id"], name: "index_epics_on_project_id"
add_index "epics", ["target_process_entity_id"], name: "index_epics_on_target_process_entity_id"
create_table "features", force: :cascade do |t|
t.integer "project_id"
t.integer "epic_id"
t.integer "target_process_entity_id"
end
add_index "features", ["epic_id"], name: "index_features_on_epic_id"
add_index "features", ["project_id"], name: "index_features_on_project_id"
add_index "features", ["target_process_entity_id"], name: "index_features_on_target_process_entity_id"
create_table "projects", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "target_process_entity_id"
end
add_index "projects", ["id"], name: "index_projects_on_id"
add_index "projects", ["target_process_entity_id"], name: "index_projects_on_target_process_entity_id"
create_table "target_process_entities", force: :cascade do |t|
t.string "type", null: false
t.string "name"
t.text "description"
t.integer "source_remote_id"
t.float "numeric_priority"
t.integer "owner"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "cloned_remote_id"
t.string "resource_type"
t.integer "project_id"
end
create_table "user_stories", force: :cascade do |t|
t.integer "project_id"
t.integer "feature_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "target_process_entity_id"
end
add_index "user_stories", ["feature_id"], name: "index_user_stories_on_feature_id"
add_index "user_stories", ["project_id"], name: "index_user_stories_on_project_id"
add_index "user_stories", ["target_process_entity_id"], name: "index_user_stories_on_target_process_entity_id"
end
While Epic and Feature both have a project_id, an instance of Feature does not have an epic_id attribute; attempting to assign an epic to feature blows up:
[20] pry(main)> epic = Epic.new
=> #<Epic:0x007fcab6c80590
id: nil,
type: "Epic",
name: nil,
description: nil,
source_remote_id: nil,
numeric_priority: nil,
owner: nil,
created_at: nil,
updated_at: nil,
cloned_remote_id: nil,
resource_type: "Epic",
project_id: nil>
[21] pry(main)> feature = Feature.new
=> #<Feature:0x007fcab6d3ba48
id: nil,
type: "Feature",
name: nil,
description: nil,
source_remote_id: nil,
numeric_priority: nil,
owner: nil,
created_at: nil,
updated_at: nil,
cloned_remote_id: nil,
resource_type: "Feature",
project_id: nil>
[22] pry(main)> epic.save
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "target_process_entities" ("type", "resource_type", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["type", "Epic"], ["resource_type", "Epic"], ["created_at", "2015-10-02 15:18:13.351578"], ["updated_at", "2015-10-02 15:18:13.351578"]]
(4.6ms) commit transaction
=> true
[23] pry(main)> feature.epic = epic
ActiveModel::MissingAttributeError: can't write unknown attribute `epic_id`
from /Users/kcallahan/.rbenv/versions/2.0.0-p647/lib/ruby/gems/2.0.0/gems/activerecord-4.2.4/lib/active_record/attribute.rb:138:in `with_value_from_database'
[24] pry(main)>
I realize it is extremely possible I am either doing something wrong or have made a poor design decision; any input is hugely appreciated as I've not been able to find anything on this and have been banging my head against it for days!
OK, I got it working almost by mistake! I added the xxx_id columns to the target_process_entities table. I assumed that the STI tables would be able to respond to the relationship definitions, though my understanding of the inner workings of STI and relationships are rusty and incomplete at best...
I may be wrong but it looks like your Feature table is a join table for many to many relationship between Project and Epic.
If that's the case, your models might look like this
class Project < TargetProcessEntity
has_many :features
has_many :epics, through: :features
end
class Epic < TargetProcessEntity
has_many :features
has_many :projects, through: :features
end
class Feature < TargetProcessEntity
belongs_to :project
belongs_to :epic
has_many :user_stories
end
the source is implied if you use the same name
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

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.

Has Many Through, Stack Level Too Deep?

I'm playing with my first has_many through relationship. I have three models. Here they are.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pools
has_many :games,
through: :pools
end
~
class Pool < ActiveRecord::Base
has_many :users
has_many :games,
through: :users
end
~
class Game < ActiveRecord::Base
belongs_to :user
belongs_to :pool
end
Should this set up allow me to do the following in IRB?
u=User.first
u.games
When I do that, I get the following error.
SystemStackError: stack level too deep
from /Users/ShiftedRec/.rvm/rubies/ruby-2.0.0-p353/lib/ruby/2.0.0/irb/workspace.rb:86
Maybe IRB bug!
I should be able to do u.games, and u.pools?
it would also be nice to do a pool.users and pool.games.
How would I have to change my model set up so I can get access to those methods..?
I've been reading around but the has_many through is kind of confusing.
Here is my schema as well
ActiveRecord::Schema.define(version: 20140408165647) do
create_table "games", force: true do |t|
t.integer "pool_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "games", ["pool_id", "user_id"], name: "index_games_on_pool_id_and_user_id", unique: true
create_table "pools", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id" #vestige of old approach
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.datetime "created_at"
t.datetime "updated_at"
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
I appreciate the help!
You were getting the error because your associations are defined incorrectly. Rails goofed up and went in recursion.
Update your models Pool and User as below:
class Pool < ActiveRecord::Base
has_many :games
has_many :users,
through: :games
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :games
has_many :pools,
through: :games
end
You are trying to create M-M Relationship between User and Pool through Game.
Also, remove user_id from pools table as you already have association through join table games.
Also, since you are on rails console(not IRB) after making above changes, reload the rails environment in console using reload! command.
Read more about has-many-through-associations in Rails guides.

Devise login validation failure

I rails3.0.4 and devise installed with following users table
create_table "users", :force => true do |t|
t.string "login"
t.string "email"
t.string "crypted_password", :null => false
t.string "password_salt", :null => false
t.string "persistence_token", :null => false
t.string "single_access_token", :null => false
t.string "perishable_token", :null => false
t.datetime "created_at"
t.datetime "updated_at"
t.string "database_authenticatable", :null => false
t.string "recoverable"
t.string "rememberable"
t.string "trackable"
t.string "reset_password_token"
end
The users table was not created by devise but I added the necessary
columns in migration.
And following settings in config/initializers/devise.rb
config.authentication_keys = [ :email]
But every time I try to sign up as a new user it throws:
2 errors prohibited this user from being saved:
Login is too short (minimum is 3 characters)
Login should use only letters, numbers, spaces, and .-_# please.
Any ideas why it could happen?
Yes, the login is too short and/or contains the wrong characters :)
You enabled the devise built-in validations, and that's what they validate. To use your own validations remove :validatable from your devise inclusion in app/models/user.rb
If you're interested, the validation used by devise is defined in lib/devise/models/validatable.rb in the devise gem.
Should the validation not be the problem you'll have to show the log of a user creation request, so that we can see what parameters are submitted.
EDIT: Oh I see, I misread - it's about "login". Well, see comments below.

Resources