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

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).

Related

The child object is not saved without any errors

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.

Ember: how to create a join table model using Rails API

I'm using Rails5 API and Ember 3.
On the Rails side the model I'd like to save is defined as follows:
class ShopLanguage < ApplicationRecord
belongs_to :shop
belongs_to :language
end
Here is the Shop model:
class Shop < ApplicationRecord
has_many :shop_languages, inverse_of: :shop, dependent: :destroy
has_many :languages, through: :shop_languages
end
Here is the ShopLanguage serializer:
class ShopLanguageSerializer < ActiveModel::Serializer
attributes :id, :shop_id, :language_id, :modified_by
belongs_to :shop
belongs_to :language
end
The problem is that when I'm trying to create a new shop_languagefor a specified shop from Ember:
# controllers/shop-languages.js
actions: {
saveLanguage(aLanguage) {
let shopLanguage = this.store.createRecord('shop-language', {
language: aLanguage,
shop: this.get('currentShop.shop'),
modifiedBy: this.get('currentUser.user').get('username')
});
shopLanguage.save().then(function() {
this.get('flashMessages').info('New language added with success');
});
the language_id is not passed in in params hash in the ShopLanguagesController on the Rails side:
# shop_langauges_controller.rb
class ShopLanguagesController < ApplicationController
before_action :find_shop
before_action :find_language, only: [:create, :destroy]
def create
#shop.languages << #language
json_response(#shop.languages, :created)
end
private
def language_params
params.permit(:language_id, :shop_id, :id, :modified_by)
end
def find_shop
#shop = Shop.find(params[:shop_id])
end
def find_language
#language = Language.find_by!(id: params[:language_id])
end
end
When I check the URL hit by Ember app, it seems to be OK:
POST http://localhost:4200/shops/613/languages
The error comes from find_language method because language_id is null.
Why so ? What is wrong with that ? Thank you

Couldn't find <Model> with ID=14 for Property with ID=1

when update, i got error like this.
Couldn't find PropertyAcceptanceCriterion with ID=14 for Property with ID=1
this error occur when off the checkbox, and update(save).
what should i do next,
model definition is
class PropertyAcceptance < ActiveRecord::Base
belongs_to :property
belongs_to :property_acceptance_criterion
end
class PropertyAcceptanceCriterion < ActiveRecord::Base
attr_accessible :name
has_many :property_acceptances, dependent: :destroy
has_many :properties, through: :property_acceptances
end
class Property < ActiveRecord::Base
attr_accessible :rent
attr_accessible :room_no
attr_accessible :property_acceptance_criterions_attributes
attr_accessible :property_acceptance_criterion_ids
has_many :property_acceptances, dependent: :destroy
has_many :property_acceptance_criterions, through: :property_acceptances
accepts_nested_attributes_for :property_acceptance_criterions, reject_if: lambda { |a| a[:name].blank? }
end
view definition is
= simple_nested_form_for #property do |f|
= f.input :room_no, input_html: {class: 'span2'}
= f.input :rent, input_html: {class: 'span2'}
= f.association :property_acceptance_criterions, as: :check_boxes
= f.simple_fields_for :property_acceptance_criterions do |c|
= c.input :name, label: "add for #{t('activerecord.attributes.property.property_acceptance_criterions')}" if c.object.new_record?
controller definition is
class Insurance::PropertiesController < Insurance::InsuranceController
before_filter :load_property, only: [:edit, :update]
before_filter :new_property, only: [:new, :create]
def new
#property.property_acceptance_criterions.build
end
def create
#property.attributes = params[:property]
if #property.save
redirect_to #property, success: t('activerecord.flash.property.actions.create.success')
else
render :new
end
end
def edit
#property.property_acceptance_criterions.build
end
def update
if #property.update_attributes(params[:property]) # ← error occur
redirect_to #property, success: t('activerecord.flash.property.actions.update.success')
else
render :edit
end
end
private
def load_property
#property = Property.find(params[:id])
end
def new_property
#property = Property.new
end
end
error is
Couldn't find PropertyAcceptanceCriterion with ID=14 for Property with ID=1
params is
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"+Zx7l7mAbX12PSO873x5NDxNOIeEe6bEDEdVnys+a98=",
"property"=>{
"room_no"=>"000",
"rent"=>"80000",
"property_acceptance_criterion_ids"=>["13", "25", ""],
"property_acceptance_criterions_attributes"=>{
"0"=>{"id"=>"13"}, "1"=>{"id"=>"14"}, "2"=>{"id"=>"25"}, "3"=>{"name"=>""}
},
"commit"=>"update",
"id"=>"1"}

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

Rails 3 - trying to create polymorphic has_one association in console

Here's my code:
Models:
class Article < ActiveRecord::Base
attr_accessible :title, :author, :content, :imageable_attributes
has_one :image, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :image, allow_destroy: true
validates_presence_of :title, :content, :author
end
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
attr_accessible :image, :caption, :imageable_id, :imageable_type, :article_ref
validates_presence_of :image
belongs_to :imageable, :polymorphic => true
end
Here's what I've tried in console:
article = Article.create!(title: "test", content: "test", author: "test", image_attributes: {image: "test.jpg", caption: "test caption"})
This creates an Article without errors, but if I call:
article.image
I get:
=> nil
If I type in console:
article = Article.new(title: "test", content: "test", author: "test")
article.build_image(image: "test.jpg")
I get:
=> Validation failed: Image image can't be blank
Any help greatly appreciated, I'm very confused!
I believe it's necessary to supply the attachment itself, rather than just the path. As an example,
i = Image.new(
:image => File.join(Rails.root, "test.jpg")
)
i.image
# =>
but
i = Image.new(
:image => File.open(File.join(Rails.root, "test.jpg"))
)
i.image
# => /uploads/tmp/20120427-2155-1316-5181/test.jpg
It's not necessary to use File.open when saving using Multipart POST, though.