The child object is not saved without any errors - ruby-on-rails-5

Ruby version 2.4.5-p335 (2018-10-18) [x86_64-linux]
Rails version 5.2.2
class TemplateCategory < ActiveRecord::Base
#self.table_name = "rt_template_categories"
has_many :templates, dependent: :delete_all
validates :title, presence: true, length: { minimum: 2 }
end
class Template < ActiveRecord::Base
#self.table_name = "rt_templates"
belongs_to :template_category
validates :body, presence: true, length: { minimum: 12 }
end
class TemplatesController < ApplicationController
def create
#template_category = TemplateCategory.find(params[:template_category_id])
#template = #template_category.templates.create(template_params)
##template_category.templates << Template.create
##template = Template.new(body:"qqq qqq qqq", template_category_id: "4")
##template.save
#sql = "INSERT INTO redmine.templates (template_category_id, body) VALUES('#{params[:template_category_id]}','#{params[:template][:body]}');"
#records_array = ActiveRecord::Base.connection.execute(sql)
redirect_to controller: "template_categories", action: "show", id: #template_category.id
end
private
def template_params
params.require(:template).permit(:body)
end
end
very simple classes, very simple controller, no errors in the log, but the object is not saved. I've tried different options, but they all don't work, except for directly executing the SQL query.

length: { minimum: 12 } - here's the reason!
I completely forgot about this restriction when I was testing the work.

Related

Rails 5: validate HABTM association

How is it possible to validate an HBTM association ?
For example you have the following 2 models with HABTM relation:
class Country < ApplicationRecord
has_and_belongs_to_many :languages
validates :code, uniqueness: { case_sensitive: false }
end
class Language < ApplicationRecord
has_and_belongs_to_many :countries
validates :tag, presence: true, uniqueness: { case_sensitive: false }
end
How to validate a language before adding it to country languages ?
I have an idea to do smth like this:
def check_for_existing_language(language)
languages.include?(language) == true
end
But where to put this callback method ? In before_save of Country model ?

Undefined Method Error in ruby on rails, when trying to produce a nested json format using to_json?

This is the product model in my gems lib s4s models folder:
module S4s
module Models
class Product < ActiveRecord::Base
self.table_name = 'product'
has_many :images, foreign_key: :product_id, class_name: 'S4s::Models::ProductImage'
has_many :primary_images, -> { where(primary_image: true) }, foreign_key: :product_id, class_name: 'S4s::Models::ProductImage'
has_one :image, foreign_key: :product_id, class_name: 'S4s::Models::ProductImage'
has_many :product_images, foreign_key: :product_id, class_name: 'S4s::Models::ProductImage'
end
end
end
This is the product_image.rb file in my gems lib s4s models folder:
require 'paperclip'
module S4s
module Models
class ProductImage < ActiveRecord::Base
self.table_name = 'product_images'.freeze
include S4s::Models::Concerns::Upload
TYPE_HIGH_RESOLUTION = 'highResolution'
TYPE_ADDITIONAL = 'additional'
IMAGE_VERSIONS = %w|mini small medium xs sm fullxs fullsm large|
attr_accessor :image_file_size
alias_attribute :image_file_name, :original_file_name
alias_attribute :image_content_type, :file_ext
alias_attribute :image_updated_at, :updated_at
belongs_to :product, foreign_key: 'product_id'.freeze, class_name: 'S4s::Models::Product'.freeze
belongs_to :color, foreign_key: 'color_id'.freeze, class_name: 'S4s::Models::Dictionary::Color'.freeze
validates :title, presence: true
scope :additional, -> { where(image_type: TYPE_ADDITIONAL) }
scope :high_resolution, -> { where(image_type: TYPE_HIGH_RESOLUTION) }
scope :primary_images, -> { where(primary_image: true) }
after_initialize :set_default_value
after_save :set_product_colors!
add_attachment :image,
styles: {
mini: ['100x100#', :jpg],
small: ['220x220#', :jpg],
medium: ['380x380#', :jpg],
xs: ['240x240', :jpg],
sm: ['348x348#', :jpg],
fullxs: ['480x480#', :jpg],
fullsm: ['768x768#', :jpg],
large: ['1000x1000', :jpg],
},
path_block: -> (style) { self.build_path(style) },
matches: /(png|jpe?g|gif)/i
# Populate file_name attribute with the current title
before_image_post_process :set_file_name!
public
def url(type = 'mini')
return nil unless self.product_id.present?
image.url(type)
end
def urls
Hash[IMAGE_VERSIONS.map { |v| [v, self.url(v)] }]
end
def as_json(opts = {})
{
id: self.id,
is_primary_image: primary_image?,
product_id: self.product_id,
title: self.title,
color: 'n/a',
sku: self.sku,
position: self.position,
image_type: self.image_type,
urls: self.urls
}
end
def build_path(style)
return nil if product.nil?
build_asset_path(style, !(new_system? || title_used?))
end
private
def build_asset_path(style, old_format = false)
"/products/#{product_id}/#{build_slug(old_format)}-#{style}.#{_find_extension(image_content_type)}"
end
def build_slug(old_format)
if old_format && !file_name.present?
"#{product.name.parameterize}#{position > 0 ? "-#{position}" : ''}"
else
file_name
end
end
def set_product_colors!
_colors = self.product.images.map(&:color).compact
if self.product.colors.map(&:id).sort != _colors.map(&:id).sort
self.product.update_attribute :colors, _colors
end
end
def set_file_name!
self.file_name = SecureRandom.hex(20)
self.new_system = true
end
def set_default_value
self.position ||= 0
self.new_system = true if self.new_system.nil?
end
end
end
end
The Logic is that we are calling these models to different apps using S4s::Models::ModelName
Below is the controller file that I am using to render json(this controller is in another app):
class HalfsController < ApplicationController
def index
#hotspot = S4s::Models::Product.all
render json: #hotspot.to_json(include: :product_images)
end
...
end
I need a nested format of Product_image objects inside product object.
I am New to ruby and rails framework, please help me out.
note: I have tried all format to_json such as :product_images and :product_image.. nested is working for many other models in gems but these are not working for product and product_images.. they have used paperclip to upload and generate images url
Try render json: #hotspot.as_json(include: :product_images)
I know that to_json calls as_json internally, but if you override to_json it will not include the associations. (I think).

Activerecord adds additional conditions in query

For example I am doing something like this:
PlatformInformer.where(1)
and result query via
PlatformInformer.where(1).to_sql
is
SELECT `platform_informers`.* FROM `platform_informers` WHERE `platform_informers`.`platform` = 'android' AND `platform_informers`.`email` = 'voldemar#klops.ru' AND (1)
I didn't ask to add email and platform fields in where clause!
This problem causes when I am executing code inside PlatforInformer model methods. Default scope is doesn't set. What is the root of evil?
Rails 3.2.13
UPDATE:
class PlatformInformer < ActiveRecord::Base
include HasUniqueGenerator
attr_accessible :email, :platform,:secret_code,:activated,:invitation_sent
before_create :init_secret_code
PLATFORMS = %w(windows macos android ios)
def self.PLATFORMS
PLATFORMS
end
validates :platform, :presence => true,:inclusion => { :in =>PLATFORMS }
validates :email, :presence => true, :email => true
validates_uniqueness_of :email, scope: :platform
scope :confirmed, Proc.new { where(:activated => true) }
def several_platforms?
PlatformInformer.confirmed.find_all_by_email(self.email).count > 0
end
def send_confirmation
if already_subscribed?
activate!
else
PlatformInformerMailer.inform_me(self.id).deliver
end
end
def activate!
PlatformInformer.where(:email=>self.email).update_all(:activated=>true)
end
private
def init_secret_code
gen_unique_code :secret_code, 16
end
def already_subscribed?
PlatformInformer.confirmed.where(email: self.email).any?
end
end
Problem was in using first_or_create method, that created virtual scope with email and platform attributes.

Undefined method 'build' in rails 3

I am getting a "NoMethodError in ProjectsController#create" with the following code:
def create
#project = current_user.project.build(params[:project])
if #project.save
flash[:success] = "Project created!"
redirect_to root_url
end
end
I have tried using #project = current_user.project.create(params[:project]) as well, but I get the same error, albeit for .create.
My Project model looks like this:
class Project < ActiveRecord::Base
attr_accessible :title,
:sub_title,
:desc,
:category
validates :user_id, presence: true
validates :title, presence: true, length: { maximum: 35 }
validates :category, presence: true
belongs_to :user
...
end
and my User model looks like this:
class User < ActiveRecord::Base
attr_accessible :name,
:surname,
:email,
:email_confirmation,
:password,
:password_confirmation
has_secure_password
has_one :project
...
end
From what I can tell, this should create a new Project with an association to the user.id and project.user_id. Any ideas why I get the error instead of successful creation?
For has_one associations you want:
#project = current_user.build_project(params[:project])
The same pattern is used for create:
#project = current_user.create_project(params[:project])
If you look at the has_one documentation they list the methods that get created when you declare the association.

Rails Rspec & FactoryGirl testing Association

I have to model's where I accept Nested Attributes. I would like to build a test to make sure the nested attribute cant be blank etc. I really don't understand how I can make the test.
My two simple models:
# SeoMapping Model
class SeoMapping < ActiveRecord::Base
belongs_to :mappingtable, :polymorphic => true
attr_accessible :seo_url
validates :seo_url, :presence => true, :uniqueness => true
end
# Page Model
class Page < ActiveRecord::Base
has_one :seo_mappings, :as => :mappingtable, :dependent => :destroy
accepts_nested_attributes_for :seo_mappings
attr_accessible :content, :h1, :meta_description, :title, :seo_mappings_attributes
.........
end
Here are my factories for Page and Seo:
FactoryGirl.define do
factory :page do |f|
seo_mapping
f.title { Faker::Name.name }
f.h1 { Faker::Lorem.words(5) }
f.meta_description { Faker::Lorem.words(10) }
f.content { Faker::Lorem.words(30) }
end
end
FactoryGirl.define do
factory :seo_mapping do |f|
f.seo_url { Faker::Internet.domain_word }
end
end
And my tests:
require 'spec_helper'
describe Page do
it "has a valid factory" do
expect(create(:page)).to be_valid
end
# Cant get this spec to work?
it "it is invalid without a seo_url" do
page = build(:page)
seo_mapping = build(:seo_mapping, seo_url: nil)
page.seo_mapping.should_not be_valid
# expect(build(:page, :seo_mapping_attributes[:seo_url] => nil)).to_not be_valid
end
it "is invalid without a title" do
expect(build(:page, title: nil)).to_not be_valid
end
...............
end
Usually for this sort of thing I use a gem called shoulda_matchers. It lets you simply assert that your model validates presence of specific attributes.
it { should validate_presence_of(:seo_url) }
it { should validate_uniqueness_of(:seo_url) }
If you don't want to use the gem, try something like this:
seo_mapping = build(:seo_mapping, seo_url: nil)
page = build(:page, seo_mapping: seo_mapping)
page.should_not be_valid