Rails amoeba gem doesn't copy many-to-many relation - ruby-on-rails

I have a problem with copying database records. I have a simple model User, that contains one-to-many relation with Language model and many-to-many relation with Skill model. I wanted to use amoeba gem to copy records with all associations. One-to-many copying works fine, but many-to-many doesn't copy at all.
Here's the code of User and Skill model:
user.rb
class User < ActiveRecord::Base
belongs_to :language
has_and_belongs_to_many :skills
validates_presence_of :email, :name
validates :email,
presence: { with: true, message: "cannot be empty" },
uniqueness: { with: true, message: "already exists in database" }
amoeba do
enable
end
end
skill.rb
class Skill < ActiveRecord::Base
has_and_belongs_to_many :users
end
I have also migration files that crates users, skills and skills_users tables:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false
t.references :language
t.timestamps null: false
end
end
end
.
class CreateSkills < ActiveRecord::Migration
def change
create_table :skills do |t|
t.string :name
t.timestamps null: false
end
end
end
.
class AddUsersSkillsTable < ActiveRecord::Migration
def change
create_table 'skills_users', :id => false do |t|
t.column :user_id, :integer
t.column :skill_id, :integer
end
end
end
Controller action 'show' in users_controller looks like this:
def copy
#user_copy = #user.dup
if #user_copy.save(validate: false)
redirect_to action: "index"
end
end
I tried copying relations in user.rb like this, but it didnt work:
amoeba do
enable
clone [:skills]
end
What may cause the problem?

Ok, I found the mistake. I wrote
#user_copy = #user.dup
in the controller file instead of
#user_copy = #user.amoeba_dup

Related

Rails belongs to association method undefined

Hi i'm having a problem with a rails association. I have the Table Users and the table Roles. Here are my migrations:
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :email
t.string :password_digest
t.belongs_to :role, index: true, foreign_key: true
t.timestamps
end
end
end
class CreateRoles < ActiveRecord::Migration[5.2]
def change
create_table :roles do |t|
t.string :name
t.string :code
t.timestamps
end
end
end
Im having the problem when I create a user with a role that I had previously created
Role.create(name: 'Super Admin', code: 'super_admin')
User.create(email: 'a#b.com', password: 'abcdefg', role_id: 1)
When I try to do User.first.role I get that the method role is undefined. As far as i know When i do that i should get an active record with the role.
What i am doing wrong. Please Help
You need to add the relation to your model. In user.rb:
class User < ApplicationRecord
belongs_to :role
# other code
end
It will generate the ActiveRecord methods you are looking for.

Rails uniqueness constraint on two column values in different models

I want to know the best way to have an uniqueness constraint enforced on two related model attributes in rails that are both no primary keys
class Parent > ApplicationRecord
has_many :children
:name
end
class Child > ApplicationRecord
:name
end
I want to enforce that (parent.name, child.name) is unique for every parent. e.g.
(parent1, child1) and (parent2, child1) is allowed
(parent1, child1) and (parent1, child1) is a violation
Ideally, I would enforce this in Postgres, however I have only seen the option to add uniqueness constraints on multiple columns of the same table.
Alternatively, I have written a custom validator for rails that does what I want, but this is cumbersome. There needs to be a better solution...
For completeness, here is the constraints validator which requires one to add a children function to a model returning the list of children.
class NamePairValidator < ActiveModel::Validator
def validate(record)
record.children.values.each do |model_children|
names = model_children.to_a.collect {|model| model.name}
if (names.select{|name| names.count(name) > 1 }.size > 0)
record.errors[:name] << 'Path leading to this resource has no unique name'
end
end
end
end
(in Parent.rb)
def children
{children: :children}
end
Migrations:
class CreateDomains < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name
t.string :domain_type
t.timestamps
end
end
end
class CreateSubjects < ActiveRecord::Migration[5.0]
def change
create_table :subjects do |t|
t.string :name
t.string :subject_type
t.timestamps
end
end
end
class CreateJoinTableDomainSubject < ActiveRecord::Migration[5.0]
def change
create_join_table :domains, :subjects do |t|
t.index [:domain_id, :subject_id]
t.index [:subject_id, :domain_id]
end
end
end
I just used similar one in my code
validates :child_name, uniqueness: { scope: :parent_id }
More..
(i) https://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
(ii) Validate uniqueness of multiple columns
Insipered by the-has-many-through-association of the official doc of ruby on rails:
class CreateAppointments < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name, null: false
t.string :domain_type
t.timestamps
end
create_table :subjects do |t|
t.string :name, null: false
t.string :subject_type
t.timestamps
end
create_table :fields do |t|
t.belongs_to :domain, index: true
t.belongs_to :subject, index: true
t.timestamps
end
end
end
Note
I took the initative to rename your model JoinTableDomainSubject by Field to be more readable.
I also force name field not be nil to check uniqueness.
(adding null: false in migrations files and validates :name, presence: true in both models)
Now the dedicated classes:
class Subject < ApplicationRecord
has_many :fields
has_many :domains, through: :fields
validates :name, presence: true
end
class Domain < ApplicationRecord
has_many :fields
has_many :subjects, through: :fields
validates :name, presence: true
end
class Field < ApplicationRecord
belongs_to :domain
belongs_to :subject
validate :domain_and_subject_names_uniqueness
private
def domain_and_subject_names_uniqueness
if class.includes(:domain, subject)
.where(domain: { name: domain.name }, subject: { name: subject.name })
.exists?
errors.add :field, 'duplicity on names'
end
end
end
Since the models are associated, I can use Field.first.domain to access Domain model of a given Field and vice versa.

ActiveModel::MissingAttributeError in Controller #create

I have a rails application with
1)User Model
class User < ActiveRecord::Base
has_secure_password
has_many :projects
end
2) Project Model
class Project < ActiveRecord::Base
belongs_to :user
end
3) CreateUser in db/migrate
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.string :password_digest
t.references :projects
t.timestamps null: false
end
end
end
4) CreateProject in db/migrarte
class CreateProjects < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :name
t.string :description
t.references :users
t.timestamps null: false
end
end
end
Now in my Controller, I have a function
def create
#project = Project.new(project_params)
#user = User.find(session[:user_id])
if #project.save
#user.projects << Project.find(#project.id)
redirect_to '/'
else
redirect_to '/project/create'
end
end
But when i call http://localhost:3000/project/new, I receive following error :-
-NoMethodError in ProjectController#create
-undefined method `projects' for # User
with
#user.projects << Project.find(#project.id)
highlighted in the extracted source.
Am I entering the record into has_many relationship correct, or is my syntax wrong?
I ran the following code in the console the server,
user = User.find(1)
user.projects
I received this error message:
NoMethodError: undefined method `projects' for #<User:0x00000001f5b508>
from /home/harshil/.rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/attribute_methods.rb:433:in `method_missing'
from /home/harshil/.rvm/gems/ruby-2.2.1/gems/activemodel-4.2.4/lib/active_model/attribute_methods.rb:433:in `method_missing'
Thanks
It seems the CreateUser migration is incorrect. It should not reference the projects. Projects should reference the User, which you have done correctly.
I believe this is confusing ActiveRecord
try removing t.references :projects from the UserCreate migration and try again

ActiveRecord: find appropriate translation

I have models:
Category:
class Category < ActiveRecord::Base
has_many :entities, as: :resourcable
end
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.text :short_descr
t.text :full_descr
t.timestamps
end
end
end
Language:
class Language < ActiveRecord::Base
has_many :entities, as: :resourcable, dependent: :destroy
validates :code, uniqueness: true
end
class CreateLanguages < ActiveRecord::Migration
def change
create_table :languages do |t|
t.string :name
t.string :code
t.timestamps
end
end
end
And Entity:
class Entity < ActiveRecord::Base
belongs_to :language
belongs_to :resourcable, polymorphic: true
end
class CreateEntities < ActiveRecord::Migration
def change
create_table :entities do |t|
t.integer :language_id
t.string :name
t.text :short_descr
t.text :full_descr
t.references :resourcable, polymorphic: true
t.timestamps
end
end
end
In Categories there are default values for fields (short_descr, full_descr), In Entities there are translations for this fields. I need to render as json all Categories with appropriate translations: at first, I need to take Language with appropriate code (for example ru), next, I need to find all language Entities for this language, next, if Entity have filled short_descr and full_descr I need to render Category with this values, else I need to render the Category with default values (this values in Categories table). How to do this? I prefer ActiveRecord buy consider pure SQL.
EDITED
Now I'm trying to use gem 'squeel':
Language.joins{entities.category}.
select{coalesce(entities.short_descr, categories.short_descr)}.
where{languages.code == 'en'}
but it doesn't work (undefined methodshort_descr' for nil:NilClass`). There is the problem?
Entity.joins(:language, :category).
select('categories.*, coalesce(entities.short_descr, categories.short_descr) as short_descr,
coalesce(entities.full_descr, categories.full_descr) as full_descr').
where('languages.code = ?', 'en')

Rails - has_many relation causes NoMethodError

I've the following migrations and models:
class CreatePlatforms < ActiveRecord::Migration
def change
create_table :platforms do |t|
t.integer :user_id
t.string :name
t.string :platform_id
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :first_name
t.string :last_name
t.string :gender
t.date :birthday
t.timestamps
end
end
end
class Platform < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :email, :first_name, :last_name, :gender, :birthday
has_many :platforms
end
With this definition I can create users and platforms:
user = User.new
platform1 = Platform.new
platform2 = Platform.new
And even I can associate users to platforms:
platform1.user = user
But when I try to associate platforms to users or get the platforms from a users it crashes:
user.platforms << user or user.platforms
NoMethodError: undefined method `platforms' for #<User:0x007f8cfd47a770>
As it turns out, the problem is the database field platform_id. This messes up rails. Simply delete it and it'll work.

Resources