Access to parent from embedded document on creation (Mongoid) - ruby-on-rails

This trick works with "has_many" relation, but fails with "embeds_many". Any ideas?
class Country
include Mongoid::Document
field :name, type: String
embeds_many :cities
end
class City
include Mongoid::Document
field :name, type: String
field :full_name, type: String, default: ->{ "#{name}, #{country.name}" }
embedded_in :country
end
1.9.3p392 :025 > c = Country.find_or_create_by(name: 'foo')
=> #<Country _id: foo, name: "foo">
1.9.3p392 :026 > c.cities.find_or_create_by(name: 'bar')
NoMethodError: undefined method `city' for nil:NilClass
So, it fails on a line "field :full_name, type: String, default: ->{ "#{name}, #{country.name}" }" becouse country is undefined for that moment

You need to check for country first, then it will return country.name
field :full_name, type: String, default: ->{ "#{name}, " << country.name if country }
I could not get this to work with string interpolation, but append works (which concatenates country.name to str)

Related

How I need to use geo_point type with Chewy?

§Hi guys,
i have problem with geo_point type definition
My Chewy Index code:
class CoordinatesIndex < Chewy::Index
define_type Coordinate.includes( :location ) do
field :location, type: 'geo_point'
field :user_id, type: 'integer'
field :start_at
field :finish_at
field :created_at
end
end
Also I have relation coordinate has_one location, location belongs_to coordinate.
Location model fields:
lat, lon
Error tha i get:
{"type"=>"mapper_parsing_exception", "reason"=>"failed to parse field [location] of type [geo_point]", "caused_by"=>{"type"=>"parse_exception", "reason"=>"field must be either [lat], [lon] or [geohash]"}}
Why?
Thanks
For future Googler's, this is works fine:
field :location, type: 'geo_point', value: ->{ { lat: location.lat, lon: location.lon } }
instead of
field :location, type: 'geo_point'

FactoryGirl.create does not work while upgrading mongoid version from 5 to 6. Below is the issue I run into while running rspec test

This is the place where is create a test table:
factory :reward_scheme, class: RewardsModels::RewardScheme do
uid { ExpectedData::COSTA_UID }
scheme_type { "bricks" }
reward_type { "menu"}
company_address { FactoryGirl.build(:company_address) }
reward_config { FactoryGirl.build(:reward_config) }
brand { FactoryGirl.build(:brand) }
master_offers { [ FactoryGirl.build(:master_offer) ] }
master_specials { [ FactoryGirl.build(:master_special) ] }
url "http://costa.com"
after(:create) do |reward_scheme|
reward_scheme.stores << FactoryGirl.create(:store)
reward_scheme.user_cards << FactoryGirl.create(:user_card)
end
end
The logs are as follows:
CoreModels::Transaction
Failure/Error: reward_scheme.stores << FactoryGirl.create(:store)
Mongoid::Errors::Validations:
message:
Validation of RewardsModels::Store failed.
summary:
The following errors were found: Reward scheme can't be blank
resolution:
Try persisting the document with valid data or remove the validations.
# ./spec/factories/reward_scheme.rb:15:in `block (3 levels) in <top (required)>'
# ./spec/core/transaction_spec.rb:6:in `block (2 levels) in <top (required)>'
This is how the model file looks like:
module UserModels
class Store
include Mongoid::Document
include Mongoid::Timestamps
field :reward_scheme_id, type: String
field :store_id, type: String
field :store_name, type: String, default: "HQ"
field :reward_scheme_name, type:String
field :about, type: String, default: "MyGravity - loyalty begins with trust"
field :logo, type: String, default: 'https://static.mygravity.co/partners/logo.svg'
field :splash_screen_url, type: String, default: "https://static.mygravity.co/assets/SplitShire_Blur_Background_XVI.jpg"
field :awaiting_update, type: Boolean, default:false
embeds_one :location, class_name:'UserModels::Location'
embeds_one :open_hours, class_name:'UserModels::OpenHours'
embeds_one :optional, class_name:'UserModels::Optional'
embeds_many :specials, class_name:'UserModels::Special'
embeds_many :offers, class_name:'UserModels::Offer'
before_create :set_defaults
def set_defaults
self.location = UserModels::Location.new unless self.location
self.optional = UserModels::Optional.new unless self.optional
end
end
class Location
include Mongoid::Document
field :longitude, type: Float, default: -0.131425
field :latitude, type: Float, default: 51.507697
field :address_line_1, type: String, default: 'Impact Hub - Westmister'
field :post_code, type: String, default: 'SW1Y 4TE'
field :city_town, type: String, default: 'London'
embedded_in :store
end
class OpenHours
include Mongoid::Document
field :monday, type: String
field :tuesday, type: String
field :wednesday, type: String
field :thursday, type: String
field :friday, type: String
field :saturday, type: String
field :sunday, type: String
field :sunday_1, type: String
embedded_in :store
end
class Special
include Mongoid::Document
# Need this to search
field :special_id, type: Integer
field :special_uid, type: Integer
field :title, type: String
field :text, type: String
embedded_in :store
before_save :set_special_uid
def set_special_uid
self.special_uid = self.special_id
end
def attributes
# p super
# p Hash[super.map{|k,v| [(alais[k] || k), v]}]
hash = super
alais = {'special_id' => 'id'}
hash.keys.each do |k,v|
hash[ alais[k] ] = hash['special_id'].to_s if alais[k]
# Need this as special_id is mapped in the iOS to a string...
hash['special_id'] = hash['special_id'].to_s if k == 'special_id'
end
hash
end
end
class Offer
include Mongoid::Document
field :name, type: String
field :offer_id, type: Integer
field :value, type: Float, default:0.0 # monetary value
field :points, type: Integer
field :icon_url, type: String
field :icon_name, type: String
embedded_in :store
def attributes
# p super
# p Hash[super.map{|k,v| [(alais[k] || k), v]}]
hash = super
alais = {'offer_id' => 'id'}
hash.keys.each { |k,v| hash[ alais[k] ] = hash['offer_id'] if alais[k] }
hash
end
end
class Optional
include Mongoid::Document
field :email, type: String, default:""
field :twitter, type: String, default:""
field :telephone, type: String, default:""
field :wifi_password, type: String, default:""
embedded_in :store
end
end
Any leads regarding the code changes required for upgrading to mongoid 6 is highly appreciated.
Thanks

Add dependent field to Mongoid's output

I have two models:
class City
include Mongoid::Document
field :alternate_names, type: String
field :coordinates, type: Array
field :name, type: String
index({ coordinates: '2d' }, { min: -180, max: 180 })
belongs_to :country
end
and
class Country
include Mongoid::Document
field :iso_3166_code, type: String
field :name, type: String
has_many :cities
end
In the controller I use
#cities = City.where(alternate_names: /#{params[:query].downcase}/).limit(10)
to receive cities list.
Here is an JSON output for each city:
...
"country_id": {
"$oid": "56fc453eae3bbe5c2abcd933"
}
...
How can I get country instead of it's country_id?
I found a solution.
#cities = City.where(alternate_names: /#{params[:query].downcase}/).includes(:country).limit(10)
render json: #cities, except: :country_id, include: :country

Test error message when validating model with rspec

I've inherited a rails api and I'm trying to test controllers. I have an endpoint '/api/v1/vitcords' where I create new vitcords. The video model only has a validation name. So my doubt is how to test that when I create a new video without specify a name, I get the message error I want, that in this case is "Vitcord name has to be specified". Thanks.
This is the Vcord model
class Vcord
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Spacial::Document
include Concerns::Vitcord::M3u8
include Concerns::Vitcord::User
# Validations
validates_presence_of :name
# Atributes
field :name, type: String
field :location, type: Array, spacial: true
field :address, type: String
field :closed, type: Boolean, default: false
field :public, type: Boolean, default: false
field :thumb, type: String
end
This is the controller video_controller.rb
module Api::V1
class VitcordsController < ApiController
def create
user = current_resource_owner
# Validation
param! :name, String, required: true
param! :location, Hash, required: false
param! :address, String, required: false
ap params[:location]
ap params[:location][:latitude]
ap params[:location][:longitude]
# Creating
vcord = Vcord.new()
vcord.name = params[:name] if params[:name]
if params[:location] && params[:location]['latitude'] && params[:location]['longitude']
vcord.location = {
lng: params[:location]['longitude'],
lat: params[:location]['latitude']
}
end
vcord.address = params[:address] if params[:address]
vcord.creator = user
if vcord.save
vcord.members.each do |member|
Notification.generate_notification_for_vitcord_invited(vcord, member)
end
#vitcord = vcord
else
error(:internal_server_error, ["Vitcord name has to be specified"], nil)
end
end
end
And this is the spec
require 'rails_helper'
describe "POST /api/v1/vitcords" do
before(:each) do
db_drop
post "/oauth/token", {
:grant_type => 'assertion',
:assertion => TOKEN
}
#token = response_json['access_token']
end
it "sends an error if a vitcord is created with name nil" do
header 'Authorization', "Bearer #{#token}"
post '/api/v1/vitcords', {
address: "Calle Rue del Percebe",
location: {
latitude: 40.7127837,
longitude: -74.00594130000002
}
}
//This does not work but it would be something like this
expect(error).to eq("Name has to be specified")
end
end
Well, you should refactor your code, but answering your question, you can add an error to you object by doing (look that I used #vcord and not vcord):
#vcord.errors.add(:name, 'Vitcord name has to be specified')
(as you can see here http://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-add)
and on your test:
expect(assigns(:vcord).errors.name).to eq('Vitcord name has to be specified').

A single row in my .csv fails to import due to TypeError: can't convert String into Integer, though others pass

Similar to this question...
When importing a CSV, I get the following Ruby 1.9.3 error: "TypeError: can't convert String into Integer"
It reads in full:
pry(main)> Lightbulb.import
TypeError: can't convert String into Integer
from /Users/user/.rvm/gems/ruby-1.9.3-p448/bundler/gems/rails-1c2717d3f5a3/activesupport/lib/active_support/core_ext/object/try.rb:36:in `[]'
I believe this is because we're passing a string (Amazon ASIN) as an index to the array in lightbulb.rb, as well as importing other data from the Amazon Product Inventory.
It works for all but on of my 35 rows. I think it is because the data for this particular Amazon product cannot be converted to an integer? I don't really know...
This is the import process which fails with one row in the CSV (listed below) where the string cannot be converted to an integer:
# asin organization_name lumens_per_watt light_output watts life_hours light_color
def self.import
destroy_all
table = CSV.read("#{Rails.root}/vendor/data/bulb.csv", :headers => true)
table.each do |row|
ap = Product.find_or_save(row['asin'])
create(
:price => ap.lowest_new_price,
:small_image => ap.small_image,
:medium_image => ap.medium_image,
:large_image => ap.large_image,
:detail_page_url => ap.detail_page_url,
:asin => ap.asin,
:brand => row['organization_name'],
:feature => ap.feature,
:organization_name => row['organization_name'],
:label => ap.label,
:manufacturer => row['organization_name'],
:product_model => ap.product_model,
:sku => ap.sku,
:title => ap.title,
:total_new => ap.total_new,
:editorial_reviews => ap.editorial_reviews,
:efficiency => row['lumens_per_watt'],
:brightness => row['light_output'],
:actual_watts => row['watts'],
:expected_life => row['life_hours'],
:light_color => row['light_color'],
:perceived_watts => calculate_perceived_watts(row['light_output'])
)
end
end
Here is the full code for Product.find_or_save:
class Product
include Mongoid::Document
include Mongoid::Timestamps
field :asin, type: String
field :parent_asin, type: String
field :detail_page_url, type: String
field :item_links, type: Array, default: []
field :sales_rank, type: String
field :small_image, type: String
field :medium_image, type: String
field :large_image, type: String
field :image_sets, type: String, default: []
field :brand, type: String
field :ean, type: String
field :ean_list, type: Hash, default: {}
field :feature, type: Array, default: []
field :item_dimensions, type: Hash, default: {}
field :label, type: String
field :manufacturer, type: String
field :product_model, type: String
field :mpn, type: String
field :package_dimensions, type: Hash, default: {}
field :part_number, type: String
field :product_group, type: String
field :product_type_name, type: String
field :publisher, type: String
field :sku, type: String
field :studio, type: String
field :title, type: String
field :lowest_new_price, type: String
field :lowest_used_price, type: String
field :total_new, type: String
field :total_used, type: String
field :offers, type: Hash, default: {}
field :customer_reviews, type: String
field :editorial_reviews, type: String
field :similar_products, type: Array, default: []
field :nodes, type: Array, default: []
def self.find_or_save(asin)
if item = where(asin: asin).first
item
else
amazon_product_hash = AmazonApi.product(asin)
attrs = ProductWrapper.parse(amazon_product_hash)
create(attrs)
end
end
If I have one row that fails - just one! :)
B00B4CPKT4,Philips,56,730,13,25000,2700
If I remove that row, just 1 of 35, it runs perfectly. It always fails at that one row in the .csv. I have placed it first, in the middle, and last - even rewritten it to find any hidden gremlin characters.
Here is the header and first row, for example, which work flawlessly:
asin,organization_name,lumens_per_watt,light_output,watts,life_hours,light_color
B00BXG7UZ8,Cree,75,450,6,25000,2700
I tried adding .to_i to make :asin => ap.asin.to_i, but no luck!
This is the last line I see in development.log, and I see 13 instances of this line corresponding to the 13 frustrating times I tried to run Lightbulb.import with this faulty product in the bulb.csv file:
MOPED: 54.234.253.6:33037 QUERY database=heroku_app14604604 collection=products selector={"$query"=>{"asin"=>"B00B4CPKT4"}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil (5231.9772ms)

Resources