Mongoid Model won't persist has_one via embeds_one relation - ruby-on-rails-4

I'm having trouble with embeds in a mongoid4-based rails 4 app. I've been looking for an answer everywhere for the past 2 days. So here is the code.
This is a church management app, with a Service model, embedding a team and a band. Each team/band has several roles such as "presidence", "communion" that refer to a user.
My models :
class Service
include Mongoid::Document
...
embeds_one :team, autobuild: true
embeds_one :band, autobuild: true
...
accepts_nested_attributes_for :team, :band
end
class Team
include Mongoid::Document
embedded_in :service
has_one :presidence, :class_name => 'User', autosave: true
has_one :message, :class_name => 'User', autosave: true
...
end
class Band
include Mongoid::Document
has_one :lead, :class_name => 'User', autosave: true
has_one :guitar, :class_name => 'User', autosave: true
...
embedded_in :service
end
class User
include Mongoid::Document
embeds_one :profile
belongs_to :team, :inverse_of => :presidence
belongs_to :team, :inverse_of => :message
belongs_to :band, :inverse_of => :lead
belongs_to :band, :inverse_of => :guitar
def fullname
"#{profile.firstname} #{profile.lastname}"
end
def self.find_by_talent(talent)
self.where("profile.talents.name" => talent)
end
end
The services controller :
# POST /services
# POST /services.json
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render action: 'show', status: :created, location: #service }
else
format.html { render action: 'new' }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
...
def service_params
params.require(:service).permit(:date, :time, :place, :name, :theme, :team => [:id, :precedence, :message ], :band => [:id, :lead, :guitar ])
end
And the form in _form.html.erb :
<%= form_for(#service) do |f| %>
...
<%= f.fields_for #service.team do |tf| %>
<%= tf.collection_select :presidence, User.find_by_talent(:presidence), :_id, :fullname, {:include_blank => "select a person"} %>
<%= tf.collection_select :message, User.find_by_talent(:message), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
<%= f.fields_for #service.band do |bf| %>
<%= bf.collection_select :lead, User.find_by_talent(:lead), :id, :fullname, {:include_blank => "select a person"} %>
<%= bf.collection_select :guitar, User.find_by_talent(:guitar), :id, :fullname, {:include_blank => "select a person"} %>
<% end %>
...
<% end %>
When creating a service, everything seems to run fine, but this is what I get in the console :
2.0.0-p195 :001 > s = Service.last
=> #<Service _id: 52ea18834d61631e7e020000, date: "2014-02-02", time: "10:00", place: "Where it's at", name: "My great name", theme: "The service's theme">
2.0.0-p195 :002 > s.team
=> #<Team _id: 52ea18834d61631e7e030000, >
2.0.0-p195 :003 > s.team.presidence
=> nil
Why is s.team.presidence not created ? s.team looks weird, too, with an ending comma...
Here is the content of my rails log :
Started POST "/services" for 127.0.0.1 at 2014-01-30 10:16:51 +0100
Processing by ServicesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ph6lbdHC2FbiANn/fGSzHWprenj3fWKXM40Hrsc5+AM=", "service"=>{"date"=>"2014-02-02", "name"=>"My great name", "theme"=>"The service's theme", "time"=>"10:00", "place"=>"Where it's at", "team"=>{"presidence"=>"52ea18324d61631e81010000", "message"=>"52ea18324d61631e81010000"}, "band"=>{"lead"=>"52ea18324d61631e81010000", "guitar"=>"52ea18324d61631e81010000"}}, "commit"=>"Create Service"}
MOPED: 127.0.0.1:27017 COMMAND database=admin command={:ismaster=>1} runtime: 0.6610ms
MOPED: 127.0.0.1:27017 UPDATE database=service_boot_camp_development collection=users selector={"band_id"=>BSON::ObjectId('52ea18834d61631e7e010000'), "_id"=>{"$nin"=>[]}} update={"$set"=>{"band_id"=>nil}} flags=[:multi]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 0.5800ms
MOPED: 127.0.0.1:27017 INSERT database=service_boot_camp_development collection=services documents=[{"_id"=>BSON::ObjectId('52ea18834d61631e7e020000'), "date"=>"2014-02-02", "time"=>"10:00", "place"=>"Where it's at", "name"=>"My great name", "theme"=>"The service's theme", "team"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e030000')}, "band"=>{"_id"=>BSON::ObjectId('52ea18834d61631e7e010000')}}] flags=[]
COMMAND database=service_boot_camp_development command={:getlasterror=>1, :w=>1} runtime: 2.7460ms
I guess I'm doing something wrong, but I have no clue if it is in the database model or in the form... or anything else...

You will not be able to do it this way. When you create an embedded document, its _id and all of its data are embedded directly within the parent document. This is in contrast to an association, where the document with the belongs_to gets a foreign key which points to its associated parent document. So here, your User documents each have a team_id and band_id, but when the database tries to get the documents, it can't find them, since you can't query directly for embedded documents; you need the parent document first. For more, see the Mongoid documentation.
Another potential issue is that you have multiple belongs_to definitions in the User models. This will also cause an issue, because for each one of those, Mongoid will attempt to create a team_id and band_id. You should name them separately and specify a class name; perhaps names like :presiding_team and :message_team, :lead_band and :guitar_band, etc. This answer should show you what that would look like.
I would recommend making the Team and Band separate referenced documents instead of embedded documents, since you won't be able to reference users effectively while they're embedded.
Hope this helps.

Related

Rails :has_many :through sort order issue in model

I am having some weird behavior in a Rails model and I'm not quite sure why. Thanks to anyone who can point me as to what I'm missing or not understanding.
The Problem
I have a model Car with a has_many :through relationship on Part through CarsPart. I expect accessing a car's parts to be in alphabetical order. I created the model specs and the controller specs. In the controller, the parts are ordered in alphabetical order as called out in the has_many relationship. In the controller spec, they are in order. In the model spec, they are not. I don't think it's tied to the testing framework because i've debugged the running server and see the same behavior.
The Code
Car
class Car < ActiveRecord::Base
has_many :cars_parts
has_many :parts, -> { order('name asc') }, through: :cars_parts, :dependent => :destroy
accepts_nested_attributes_for :cars_parts, :allow_destroy => true
validates :make, :model, presence: true
validate :validate_cars_parts
def validate_cars_parts
errors.add(:parts, "wrong number") if self.cars_parts.size < 1
self.cars_parts.each do |car_part|
part = Part.find(car_part.part_id)
errors.add(:parts, "doesn't exist") if part == nil
end
end
end
Part
class Part < ActiveRecord::Base
belongs_to :cars_part
has_many :cars, through: :cars_parts
validates :name, presence: true
end
CarsPart
class CarsPart < ActiveRecord::Base
belongs_to :car
belongs_to :part
end
Cars Controller Spec Passes
require 'rails_helper'
RSpec.describe CarsController, type: :controller do
render_views
context 'property features' do
describe "GET #show/id returns ordered parts" do
before :each do
#p4 = FactoryGirl.create(:part, :name => 'BPart2')
#p3 = FactoryGirl.create(:part, :name => 'APart2')
#p6 = FactoryGirl.create(:part, :name => 'BPart3')
#p5 = FactoryGirl.create(:part, :name => 'APart3')
#p2 = FactoryGirl.create(:part, :name => 'BPart1')
#p1 = FactoryGirl.create(:part, :name => 'APart1')
end
it 'returns parts in alphabetical order' do
car = FactoryGirl.create(:car, make: 'Nissan', model: 'Murano', parts: [#p4, #p3, #p6, #p5, #p2, #p1])
get :show, id: car.id, format: :json
expect(response).to have_http_status(:success)
expect(response).to render_template(:show)
response_json = JSON.parse(response.body)
response_json['parts'].each do |part|
puts part['id']
end
expect(response_json['parts'].size).to eq(6)
expect(response_json['parts'][0]['id']).to eq(#p1.id)
expect(response_json['parts'][1]['id']).to eq(#p3.id)
expect(response_json['parts'][2]['id']).to eq(#p5.id)
expect(response_json['parts'][3]['id']).to eq(#p2.id)
expect(response_json['parts'][4]['id']).to eq(#p4.id)
expect(response_json['parts'][5]['id']).to eq(#p6.id)
end
end
end
end
Car Model Spec Fails
require 'rails_helper'
RSpec.describe Car, type: :model do
context 'car parts' do
before :each do
#p4 = FactoryGirl.create(:part, :name => 'BPart2')
#p3 = FactoryGirl.create(:part, :name => 'APart2')
#p6 = FactoryGirl.create(:part, :name => 'BPart3')
#p5 = FactoryGirl.create(:part, :name => 'APart3')
#p2 = FactoryGirl.create(:part, :name => 'BPart1')
#p1 = FactoryGirl.create(:part, :name => 'APart1')
end
it 'returns parts in alphabetical order' do
car = FactoryGirl.create(:car, make: 'Nissan', model: 'Murano', parts: [#p4, #p3, #p6, #p5, #p2, #p1])
expect(car).to be_valid
expect(car.errors.messages.size).to eq(0)
expect(car.parts.size).to eq(6)
car.parts.each do |part|
puts part.id
end
expect(car.parts[0]['name']).to eq('APart1')
expect(car.parts[1]['name']).to eq('APart2')
expect(car.parts[2]['name']).to eq('APart3')
expect(car.parts[3]['name']).to eq('BPart1')
expect(car.parts[4]['name']).to eq('BPart2')
expect(car.parts[5]['name']).to eq('BPart3')
end
end
end
Summary
Please help me understand why when accessing them through the model they are not ordered, but when the controller looks them up and renders them, they are.
Thank you.
EDIT
The only way I have managed to get this to work is to create a 'ordered_parts' method that does the ordering and remove the order from the has_many. I had to change all my controllers to call 'ordered_parts' instead of the 'parts' method. Surely there is a better way. If anyone knows, please help.
def ordered_parts
self.parts.order('name asc')
end

Rails 4.2 has_many through in-place editing nested form with xeditable

I have the following list of models in a Rails 4.2 app:
User
Company
Marketplace
The associations look like the following:
User.rb
class User < ActiveRecord::Base
end
Company.rb
class Company < ActiveRecord::Base
has_many :company_marketplaces
has_many :marketplaces, through: :company_marketplaces
accepts_nested_attributes_for :marketplaces, update_only: true
end
Marketplace.rb
class Marketplace < ActiveRecord::Base
has_many :company_marketplaces
has_many :companies, through: :company_marketplaces
end
I am trying to build a form to allow adding/removing marketplaces to a company with the help of in-place editing library x-editable. I was able to allow adding new marketplaces from an existing list but it fails on the removing because no data is submitted.
This is the relevant part of the view:
index.html.slim
td
= link_to company.marketplaces.join(", "), "", data: { :"name" => "marketplaces_attributes" , :"xeditable" => "true", :"url" => admin_agents_company_path(company), :"pk" => company.id, :"model" => "company", :"type" => "checklist", :"placement" => "right", :"value" => company.marketplaces.join(", "), :"source" => "/marketplaces" }, class: "editable editable-click"
And companies_controller.rb#update method looks like the following:
companies_controller.rb
def update
respond_to do |format|
if #company.update(company_params)
format.html { redirect_to #company, notice: 'Company was successfully updated.' }
format.json
format.js
else
format.html { render :edit }
format.json { render json: #company.errors, status: :unprocessable_entity }
format.js
end
end
end
Most of the resources I was able to find do not deal with in-place editing or use fields_for to work with nested_forms.
Is there a solution to handle adding/removing has_many: through: associated objects in a form without relying on fields_for?
Thanks
You can provide marketplaces_attributes in params.
Example from: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
class Member < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts
end
params = { member: {
name: 'joe', posts_attributes: [
{ title: 'Kari, the awesome Ruby documentation browser!' },
{ title: 'The egalitarian assumption of the modern citizen' },
{ title: '', _destroy: '1' } # this will be ignored
]
}}
member = Member.create(params[:member])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
Read more examples in that link above

Strong parameters with Rails 4.0 and Devise

I am using Rails 4.2 and devise.Request type will always be JSON.My registrations controller looks like this
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :configure_sign_up_params, only: [:create]
def create
if request.format(request) == "application/json"
build_resource(sign_up_params)
if resource.save
render :json => {:success => "true", :message => "Thank You For Registering."}
else
render :json => {:success => "false", :message => resource.errors.full_messages}
end
else
super
end
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:email, :password, :password_confirmation)
end
end
end
The parameters that show-up in the console is like :
Parameters: {"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "registration"=>{"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
The error it produces is that the controller doesn't recognize the email and password fields and always returns error like :
["Email can't be blank","Password can't be blank"]
Does this happen because of any foolishness that I have written in the code?Please help
Your configure_sign_up_params method has to be something like this to work.
def configure_sign_up_params
devise_parameter_sanitizer.for(:registration) do |u|
u.permit(:email, :password, :password_confirmation)
end
end
If you can see the console content what you have posted, it contains a hash with registration as a key:
"registration"=>{"email"=>"test123#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}

rails 4.1 Activeadmin with paperclip, add image button pressed no response

I want to upload images to a place in active admin dashboard by paperclip, but When i pressed "add new place picture", nothing happened. Image as below:
model/place.rb
class Place < ActiveRecord::Base
has_many :place_pictures, :dependent => :destroy,
:autosave => true
accepts_nested_attributes_for :place_pictures,
:allow_destroy => true
end
model/place_picture.rb
class PlacePicture < ActiveRecord::Base
belongs_to :place
attr_accessible :picture, :logo
has_attached_file :picture, :styles => { :medium => "300x300>", :thumb => "100x100>" },
:default_url => "/images/:style/missing.png"
validates_attachment_content_type :picture, :content_type => /\Aimage\/.*\Z/
end
admin/place.rb
ActiveAdmin.register Place do
form :html => { :enctype => "multipart/form-data" } do |f|
f.inputs "Place details" do
f.input :name
f.input :about
f.has_many :place_pictures do |ff|
ff.input :picture, :as => :file, :hint => ff.template.image_tag(ff.object.picture.url(:thumb))
ff.input :logo, as: :boolean, :label => "isLogo"
end
end
f.actions
end
index do
column :id
column :name
column :city
column :about
column "Images" do |place|
if !(place.place_pictures.empty?)
ul do
place.place_pictures.each do |img|
li do
image_tag(img.picture.url(:thumb))
end
end
end
end
end
actions
end
show do |ad|
attributes_table do
row :name
row :city
row :about
row :image do
if !(place.place_pictures.empty?)
ul do
place.place_pictures.each do |img|
li do
image_tag(img.picture.url(:thumb))
end
end
end
end
end
end
#active_admin_comments
end
end
Gemfile:
gem 'rails'#version 4.1.8
gem 'activeadmin', github: 'activeadmin'
gem "paperclip", "~> 4.2"
It worked well before on rails 3.x and active_admin 0.6.x. Any help? thanks!
UPDATE:
Finally change active_admin.js to below, it works! don't know why! hope help anyone if has this situation.
// = require jquery
// = require jquery_ujs
// = require active_admin/base
It seems the javascript that appends the fields (has_many), doesn't work. Do you have any JavaScript runtime on your system? Node.js for example.
https://github.com/sstephenson/execjs

Carrierwave + ActiveAdmin with example for file and image field. results in "stack level too deep"

I am attempting to create a file and image field on ActiveAdmin. Example.
What I have so far.
Created a Migration to add file and image columns to the database.
Created Active Admin and edited display form.
Created the model
Created 2 uploaders
My code results in "stack level too deep" error.
The weird thing is the code is exactly the same as my example which works fine.
admin/product.rb
ActiveAdmin.register Product do
permit_params :title, :image, :file
form(:html => { :multipart => true }) do |f|
f.inputs "Create Product..." do
f.input :title
f.input :image, :as => :file, :hint => f.template.image_tag(f.object.image.url(:thumb))
f.input :file, :as => :file
end
f.actions
end
end
models/product.rb
class Product < ActiveRecord::Base
mount_uploader :file, FileProductsUploader
mount_uploader :image, ImageProductsUploader
end
uploaders/file_products_uploader.rb
class FileProductsUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
uploaders/image_products_uploader.rb
class ImageProductsUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
storage :file
def store_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :resize_to_fit => [250, 250]
end
end
Error Message
SystemStackError in Admin::ProductsController#update
stack level too deep
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"ViiAmqH+S9XjP0wBSc7I2USEl1LXLw/N532Kd+uhNqc=",
"product"=>{"title"=>"First Product",
"image"=>#<ActionDispatch::Http::UploadedFile:0x50e6008 #tempfile=# <Tempfile:C:/Users/User/AppData/Local/Temp/RackMultipart20141016-7388-e5v6is>,
#original_filename="example.png",
#content_type="image/png",
#headers="Content-Disposition: form-data; name=\"product[image]\"; filename=\"example.png\"\r\nContent-Type: image/png\r\n">},
"commit"=>"Update Product",
"id"=>"1"}
Console Error message
Unpermitted parameters: utf8, _method, authenticity_token, commit, id
Completed 500 Internal Server Error in 138ms
SystemStackError (stack level too deep):
actionpack (4.1.5) lib/action_dispatch/middleware/reloader.rb:79
None of the online solutions worked for me. I solved the error changing the local temp directory
Uploader/*
def cache_dir
# should return path to cache dir
Rails.root.join 'tmp/uploads'
end