Unable to create correct Mongoid model from JSON - ruby

I'm trying to create a Mongoid model from the corresponding JSON structure.
However it does not include the embedded relation frameworks.
I'm using Mongoid 4.0. Am I doing something wrong or is this a bug?
If I store any embedded relation via store_as under a different name than the default serialization, it works as expected. Also, if I create the model in the database from JSON rather than initialize it everything's fine...
JSON input
{
"name": "MyName",
"frameworks": [
{
"name": "grails",
"runtime": "groovy",
"versions": []
}
]
}
Models
require 'mongoid'
class Vendor
include Mongoid::Document
include Mongoid::Attributes::Dynamic
# fields
field :name, type: String
# relations
embeds_many :frameworks
# validations
validates :name, presence: true
validates :frameworks, presence: true
end
class Framework
include Mongoid::Document
embedded_in :vendor
field :name, type: String
field :runtime, type: String
field :versions, type: Array
# validations
validates :name, presence: true
validates :runtime, presence: true
end
Test App
require 'json'
require 'require_relative'
require_relative 'vendor'
begin
json = JSON.parse(File.read('input.json'))
#profile = Vendor.new(json)
puts #profile.inspect
rescue JSON::ParserError => e
puts "Error: " << e.to_s
end

Related

Can't Use Validates Presence On Picture Rails 5.x

micropost.rb
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :tag1, presence: true, length: { maximum: 20 }
validates :tag2, presence: true, length: { maximum: 20 }
validates :tag3, presence: true, length: { maximum: 20 }
validates :picture, presence: true
validate :picture_size
validates :ispublic, inclusion: { in: [ true, false ] }
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB in size.")
end
end
end
microposts_controller.rb snippet:
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save!
flash[:success] = "Post created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def micropost_params
params.require(:micropost).permit(:tag1, :tag2, :tag3, :picture, :ispublic)
end
Test that is failing snippet:
tag1 = "This"
tag2 = "Is"
tag3 = "Sparta"
image = File.open("test/fixtures/p_avatar.png")
assert_difference 'Micropost.count', 1 do
post microposts_path, params: {micropost: { tag1: tag1, tag2: tag2, tag3: tag3, picture: "image",
ispublic: false }}
end
Test error:
Validation failed. Picture can't be blank.
I am using CarrierWave...based on other answers I was wondering if I can't use the presence: true validation with a picture, but I thought the #micropost.save would just store whatever as a string (varchar). For testing purposes, I just want to make sure a valid string is passed, but maybe that isn't even necessary if CarrierWave and picture_size validation is enough.
Found it. In the Rails spec, there is a function called fixture_file_upload() which does all the heavy lifting of creating the image object for me (some sort of upload to a cache it seems) and making the Model.save() method validate it and store the string in the db.
Thanks to this site it informed me a bit on image upload testing without going through FactoryBot: https://jeffkreeftmeijer.com/carrierwave-rails-test-fixtures/
Ultimately I decided I micropost doesn't require a picture (A la Twitter).

Rails 4+: where to store custom validation msgs used across models

I have custom validation messages that I use in several different models:
# models/user.rb
class Person < ActiveRecord::Base
validates :name, presence: { message: "This is a required question" }
end
# models/pet.rb
class Pet < ActiveRecord::Base
validates :name, presence: { message: "This is a required question" }
end
I'm a little familiar with the en.yml config, but that specifies messages per model:
en:
activerecord:
errors:
models:
person:
attributes:
name:
blank: "This is a required question"
pet:
attributes:
name:
blank: "This is a required question"
And that just seems very verbose and not developer-friendly.
My solution was to create an initializer file and store my custom validation messages in a hash there:
# config/initializers/custom_validation_messages.rb
CUSTOM_VALIDATION_MESSAGES = {
required: "This is a required question"
}
And then to use them as:
validates :name, presence: { message: CUSTOM_VALIDATION_MESSAGES[:required] }
My solution works, but I'm not sure if this is a correct usage of an initializer. Is there a more appropriate Rails convention for cleaning up those duplicate validation messages?
I prefer the following approach, so I could also use the translations on other places within my app (like a hint message for instance):
class Pet < ActiveRecord::Base
validates :name, presence: { message: I18n.t('common_errors.required_question' }
end
and
en:
common_errors:
required_question: "This is a required question"

Mongo, Mongoid join data from different models in Ruby on Rails

I'm adapting a Ruby on Rails application to work with MongoDB instead of PostgreSQL. I have this 2 class:
module Cms
class Content
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :text_body, type: String
field :expiration_date, type: Date
field :active, type: Boolean, default: false
validates :title, :text_body, presence: true
orderable base: 0
belongs_to :cms_content_category, class_name: 'Cms::ContentCategory'
scope :ordered, ->() { order(position: :asc) }
scope :not_expired, ->() { any_of({ expiration_date: nil }, { :expiration_date.gte => Date.today }) }
end
end
module Cms
class Submenu
include Mongoid::Document
include Mongoid::Orderable
field :title, type: String
field :position, type: String
field :external_url, type: String
field :open_as, type: String, default: '_self'
field :active, type: Boolean, default: false
delegate :url_helpers, to: :'Rails.application.routes'
validates :title, presence: true
validates :external_url, presence: true, if: Proc.new { |obj| obj.cms_content_id.nil? }
validates :cms_content_id, presence: true, if: Proc.new { |obj| obj.external_url.blank? }
orderable base: 0
belongs_to :cms_content, class_name: 'Cms::Content'
belongs_to :cms_menu, class_name: 'Cms::Menu'
scope :ordered, ->() { order(position: :asc) }
def url
self.external_url.present? ? self.external_url : url_helpers.content_show_path(self.cms_content_id)
end
end
end
The problem is that I don't know how to adapt the following SQL query to work with MongoDB.
scope :active, joins("FULL JOIN contents ON contents.id = cms_submenus.content_id")
.where("cms_submenus.active IS TRUE AND (
(cms_submenus.content_id IS NOT NULL AND ((contents.expiration_date >= :today OR expiration_date IS NULL) AND contents.archived IS NOT TRUE)) OR
(cms_submenus.external_url IS NOT NULL AND cms_submenus.external_url <> '')
)", today: Date.today)
This scope has to be called from Submenu model.
Someone has an idea on how to solve this problem?
I'm using Rails 4.2.0 with Ruby 2.2.0 and Mongoid 4.0.2 with MongoDB 2.6.8.
thank you

How to efficiently retry after failing Mongoid uniqueness validation?

I have a Mongoid model which requires two fields to be unique. I want to generate the "unique number" value and retry if it becomes not unique in the time it took to save.
class MyModel
include Mongoid::Document
include Mongoid::Timestamps
field :number
field :name
index({ number: 1 }, { unique: true, background: true })
index({ name: 1 }, { unique: true, background: true })
validates_presence_of :number, :name
validates_uniqueness_of :number, :name
def self.make!(name)
find_or_initialize_by(name: name).tap do |model|
if model.new_record?
model.number = generate_random_number
model.save! # fails validation because number is no longer unique
end
end
end
end
How can I retry when the uniqueness collision happens?
Begin
Rescue
Retry
Check
this example

Mongo object's id as uploadings dirname in CarrierWave

I'm using Sinatra with Mongoid and CarrierWave. I need to store document's attachments in /public/attachments/DOCUMENTS_ID.
Model of Mongo document:
class Dcmnt
include Mongoid::Document
store_in collection: 'dcmnts'
field :published, type: Boolean
field :name, type: String
field :description, type: String
field :additional, type: String
field :created_at, type: Date
mount_uploader :attachment, Uploader, type: String
end
And action's code:
post '/admin/create' do
params.delete 'submit'
d = Dcmnt.new(
:published => params[:published],
:name => params[:name],
:description => params[:description],
:additional => params[:additional],
:created_at => Time.now
)
d.attachment = params[:photos]
d.save
end
When I'm setting up unloader like this:
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'public/attachments/' + d.id
end
end
It doesn't works for some amzaing reason. Can you help me implement this feature?
Accessing models attributes in CarrierWave is provided via model key word
class Uploader < CarrierWave::Uploader::Base
storage :file
def store_dir
'attachments/' + model.id
end
end

Resources