How Do I Update Nested Mongo Document Attributes in Rails with Mongoid? - ruby-on-rails

(Apologies in advance if this question is short on details, I'll watch the comments and add what I can)
I have a Model with the following:
class Product
include Mongoid::Document
include Mongoid::Timestamps
#...
field :document_template, :type => Document
accepts_nested_attributes_for :document_template
Inside the Document document_template, is the following references_many, which I want to modify. Specifically, I want to change which fonts are referenced:
class Document
include Mongoid::Document
include Mongoid::Timestamps
#...
references_many :fonts, :stored_as => :array, :inverse_of => :documents
What sort of logic and details should I have in my controller and form to get this done? Please comment if you would like me to add some of the zany things I've tried; however, I haven't had any luck with any of them.
Here is a quick showing of the issue using rails console:
# Grab a Product and check how many fonts are in it's document_template
ruby-1.8.7-p302 > prod = Product.find(:first)
=> ...
ruby-1.8.7-p302 > prod._id
=> BSON::ObjectId('4d06af15afb3182bf5000111')
ruby-1.8.7-p302 > prod.document_template.font_ids.count
=> 9
# Remove a font from the font_ids array
ruby-1.8.7-p302 > prod.document_template.font_ids.pop
=> BSON::ObjectId('...') # This font id was removed from font_ids
ruby-1.8.7-p302 > prod.document_template.font_ids.count
=> 8
# Save the changes
ruby-1.8.7-p302 > prod.document_template.save!
=> true
ruby-1.8.7-p302 > prod.save!
=> true
# Instantiate a new product object of that same product
ruby-1.8.7-p302 > prod_new = Product.find(:first)
=> ...
# Confirm the _ids are the same
ruby-1.8.7-p302 > prod._id == prod_new._id
=> true
# Check to see if the changes were persisted
ruby-1.8.7-p302 > prod_new.document_template.font_ids.count
=> 9 # If the changes persisted, this should be 8.
# Grrrrr... doesn't look like it. Will the change disappear after a reload too?
ruby-1.8.7-p302 > prod.reload
=> ...
ruby-1.8.7-p302 > prod.document_template.font_ids.count
=> 9
# ಠ_ಠ ... no dice.
Updating objects using mongo (and not mongoid in rails) works as expected.
Kyle Banker has asked for some logging info, so here it is. Unfortunatly, I couldn't find a better source of logging than the output from rails server, which seems to suggest the update call is never being made. For some context here is some info from the controller:
def update_resource(object, attributes)
update_pricing_scheme(object, attributes)
update_document_template_fonts(object, attributes)
end
def update_document_template_fonts(object, attributes)
document_template = object.document_template
document_template_attributes = attributes[:document_template_attributes]
font_ids = document_template_attributes[:font_ids]
font_ids.delete("") # Removing an empty string that tags along with the font_ids.
font_ids.collect! { |f| BSON::ObjectId(f) } # Mongo want BSON::ObjectId
object.document_template.font_ids.replace font_ids
object.document_template.save!(:validate => false)
object.save!(:validate => false)
end
Here is the output from rails server when the POST is processed:
Started GET "/admin/products/4d091b18afb3180f3d000111" for 127.0.0.1 at Wed Dec 15 13:57:28 -0600 2010
Started POST "/admin/products/4d091b18afb3180f3d000111" for 127.0.0.1 at Wed Dec 15 13:57:49 -0600 2010
Processing by Admin::ProductsController#update as HTML
Parameters: {"commit"=>"Update Product", "authenticity_token"=>"QUW0GZw7nz83joj8ncPTtcuqHpHRtp1liq8fB7/rB5s=", "utf8"=>"✓", "id"=>"4d091b18afb3180f3d000111", "product"=>{"name"=>"Ho Ho Ho Flat Multiple Photo Modern Holiday Card", "document_template_attributes"=>{"id"=>"4d091b18afb3180f3d000112", "font_ids"=>["", "4d091b17afb3180f3d000023"]}, "description"=>"", "pricing_scheme_id"=>"4d091b17afb3180f3d00003b"}}
development['users'].find({:_id=>BSON::ObjectId('4d091b17afb3180f3d00009b')}, {}).limit(-1)
development['products'].find({:_id=>BSON::ObjectId('4d091b18afb3180f3d000111')}, {}).limit(-1)
development['pricing_schemes'].find({:_id=>BSON::ObjectId('4d091b17afb3180f3d00003b')}, {}).limit(-1)
MONGODB development['products'].update({"_id"=>BSON::ObjectId('4d091b18afb3180f3d000111')}, {"$set"=>{"updated_at"=>Wed Dec 15 19:57:50 UTC 2010}})
in Document#set_default_color_scheme: self.color_scheme = #<ColorScheme:0xb52f6f38>
MONGODB development['documents'].update({"_id"=>BSON::ObjectId('4d091b18afb3180f3d000112')}, {"$set"=>{"color_scheme_name"=>"green_charcoal_black", "updated_at"=>Wed Dec 15 19:57:50 UTC 2010}})
Redirected to http://localhost:3000/admin/products/4d091b18afb3180f3d000111
Completed 302 Found in 49ms
It looks like the MONGODB command to update the font_ids is completely absent...

I'm partial to believing Mongoid Issue #357 is causing the problem. The logging above suggests that an update for the product's document_template.fonts (or font_ids) which seems to match the bug description.
(sidenote: I'm a bit confused where exactly the font_ids array comes from if not the :stored_as => :array. I'm not 100% certain which I should be modifying either but since font_ids is an Array and fonts is a Mongoid::Criteria, the path of least resistance is font_ids.)
From a data standpoint, :field and :embeds_* seem to be similar in that the information is embedded in the parent document.

Mongoid has a complicated back end. Therefore, the easiest way to diagnose this is to enable the driver's logging. Then we can look at the exact messages being sent to the database in both cases, and we're sure to get an answer.
Can you attach a logger when you connect to MongoDB and then post the relevant sections of log output?

Related

Rails Digest::UUID v5 (vs) Postgresql uuid-ossp v5

I'm getting different V5 UUIDs when generating with Rails Digest::UUID and Postgresql uuid-ossp.
Rails:
[58] pry(main)> Digest::UUID.uuid_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> "db68e7ad-332a-57a7-9638-a507f76ded93"
Postgresql uuid-ossp:
select uuid_generate_v5('e90bf6ab-f698-4faa-9d0f-810917dea53a', 'e90bf6ab-f698-4faa-9d0f-810917dea53a');
uuid_generate_v5
--------------------------------------
6c569b95-a6fe-5553-a6f5-cd871ab30178
What would be the reason? I thought both should generate the same UUID when the input is the same, but it is different!
It's not an answer to the question about why Rails produces a different result, but if you want to produce v5 UUID in your Ruby code, you could use uuidtools. It returns the same result as PSQL:
~ pry
[1] pry(main)> require 'uuidtools'
=> true
[2] pry(main)> UUIDTools::UUID.sha1_create(UUIDTools::UUID.parse('e90bf6ab-f698-4faa-9d0f-810917dea53a'), 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
=> #<UUID:0x3fe09ea60dd8 UUID:6c569b95-a6fe-5553-a6f5-cd871ab30178>
[3] pry(main)>
It seems that a patch is proposed so that working string-representation of namespaces can be enabled explicitly
The new behavior will be enabled by setting the config.active_support.use_rfc4122_namespaced_uuids option to
true.
but, the patch is very recent and it could be still under test. People can be afraid it breaks things. Check
https://github.com/rails/rails/issues/37681
https://github.com/rails/rails/pull/37682/files
Meanwhile, a workaround is to pack the namespace string
ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
In your example
irb(main):037:0> n='e90bf6ab-f698-4faa-9d0f-810917dea53a'
=> "e90bf6ab-f698-4faa-9d0f-810917dea53a"
irb(main):038:0> ns=n.scan(/(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/).flatten.map { |s| s.to_i(16) }.pack("NnnnnN")
=> "\xE9\v\xF6\xAB\xF6\x98O\xAA\x9D\x0F\x81\t\x17\xDE\xA5:"
irb(main):039:0> puts Digest::UUID.uuid_v5(ns, 'e90bf6ab-f698-4faa-9d0f-810917dea53a')
6c569b95-a6fe-5553-a6f5-cd871ab30178

ActionDispatch::Http::Parameters#encode_params tries to modify frozen strings and raises on upgrade from Rails 3.0 to Rails 3.2.16

Basically, this seems to be https://github.com/rails/rails/issues/7725 reported a year ago by a guy who stopped responding (heh http://xkcd.com/979/)
and I got this when upgrading from the very last Rails 3.0 to Rails 3.2.16.
The route in question serves HTML from model Page on routes of form /en/contact for example (from config/routes.rb)
# pages
STATIC_PAGES.each do |slug, desc|
match ":language/#{slug}" => 'pages#static_page', :defaults => {:slug => slug, :language => 'en'}, :via => :get, :as => slug.underscore.to_s
end
My attempts to make a sample application that does the same thing and breaks have failed (copied relevant parts of the app into a new app, copied the Gemfile & Gemfile.lock and tried to reproduce, all went fine)
This is the stack trace: https://gist.github.com/bbozo/8315184 - not a single line from my app in it
Again, it's one of those argh, a ghost issues, if anyone has a hunch where to hunt for it, you'll make me VERY happy
:-/
Duping the key from hash iterator fixed it for me,
# pages
STATIC_PAGES.each do |slug, desc|
match ":language/#{slug}" => 'pages#static_page', :defaults => {:slug => slug.dup, :language => 'en'}, :via => :get, :as => slug.underscore.to_s
end
problem here was that STATIC_PAGES is a hash constant with String keys, it's keys are frozen. In cases of requests for which route defaults kicked in the router tried to do something with the frozen slug string stored in :defaults => {:slug => slug - something in the rails 3.0 => rails 3.2.16 changelog introduced a modification of this value and shiny exceptions happened in the ActionDispatch stack.
"Unfreezing" slug by doing slag.dup fixed the issue

What is wrong with this Sunspot Solr setup?

I am using sunspot to search my local db. After adding the gems, running the generate command, and booting up the solr server I do the following:
class Style < ActiveRecord::Base
attr_accessible :full_name, :brand_name
searchable do
text :full_name
text :brand_name
end
end
Added the above to my Style model and re-indexed (I had already indexed prior to creating this post, which is why I re-indexed to put it here)
funkdified#vizio ~/rails_projects/goodsounds.org $ rake sunspot:solr:reindex
[RailsAdmin] RailsAdmin initialization disabled by default. Pass SKIP_RAILS_ADMIN_INITIALIZER=false if you need it.
*Note: the reindex task will remove your current indexes and start from scratch.
If you have a large dataset, reindexing can take a very long time, possibly weeks.
This is not encouraged if you have anywhere near or over 1 million rows.
Are you sure you want to drop your indexes and completely reindex? (y/n)
y
[#######################################] [14/14] [100.00%] [00:00] [00:00] [53.19/s]
Then I try a search and get nothing
1.9.3p392 :003 > Style.search { fulltext 'Monkey' }.results
SOLR Request (10.4ms) [ path=#<RSolr::Client:0x0000000685ab28> parameters={data: fq=type%3AStyle&q=Monkey&fl=%2A+score&qf=full_name_text+brand_name_text&defType=dismax&start=0&rows=30, method: post, params: {:wt=>:ruby}, query: wt=ruby, headers: {"Content-Type"=>"application/x-www-form-urlencoded; charset=UTF-8"}, path: select, uri: http://localhost:8982/solr/select?wt=ruby, open_timeout: , read_timeout: , retry_503: , retry_after_limit: } ]
=> []
But, wait shouldn't it have worked and picked this up?
Style.first
Style Load (1.3ms) SELECT "styles".* FROM "styles" LIMIT 1
=> #<Style id: 54, brand_name: "Monkey", full_name "Monkey Chicken", created_at: "2013-02-01 23:25:58", updated_at: "2013-02-16 03:02:16">
Here is one more clue. I am seeing "unknown field" for brand_name (setup in Style.rb)
If you change the schema (the "searchable" block) you have to either reindex all models:
rake sunspot:solr:reindex
or reindex that specific model with a given batch size (here 500):
rake sunspot:solr:reindex[500,Style]
as per the Sunspot doco on Github (search "Reindexing Objects").
FYI, to use Style.reindex for non-schema changes, you will have to call Sunspot.commit to save changes.

Rails to_json Why are timestamps breaking, and being set to 2000-01-01T01:31:35Z

I'm building a json object in rails as follows:
#list = Array.new
#list << {
:created_at => item.created_at
}
end
#list.to_json
Problem is this gets received by the browser like so:
"created_at\":\"2000-01-01T01:31:35Z\"
Which is clearly not right, in the DB it has:
2011-06-17 01:31:35.057551
Why is this happening? Any way to make sure this gets to the browser correctly?
Thanks
You need to do some testing / debugging to see how that date is coming through.
For me, in Rails console (Rails 3.0.9, Ruby 1.9.2)
ruby-1.9.2-p180 :014 > d = Date.parse("2011-06-17 01:31:35.057551")
=> Fri, 17 Jun 2011
ruby-1.9.2-p180 :015 > #list = {:created_at => d}
=> {:created_at=>Fri, 17 Jun 2011}
ruby-1.9.2-p180 :016 > #list.to_json
=> "{\"created_at\":\"2011-06-17\"}"
i.e. it's just fine. Can you see whether the date is really ok?
The trouble lies with the way to_json escapes characters. There is a very good post on the subject here:
Rails to_json or as_json
You may need to look into overriding as_json.

ArgumentError on application requests

I've written a basic Rails 3 application that shows a form and an upload form on specific URLs. It was all working fine yesterday, but now I'm running into several problems that require fixing. I'll try to describe each problem as best as I can. The reason i'm combining them, is because I feel they're all related and preventing me from finishing my task.
1. Cannot run the application in development mode
For some unknown reason, I cannot get the application to run in development mode. Currently i've overwritten the production.rb file from the environment with the settings from the development environment to get actuall stacktraces.
I've added the RailsEnv production setting to my VirtualHost setting in apache2, but it seems to make no difference. Nor does settings ENV variable to production.
2. ArgumentError on all calls
Whatever call I seem to make, results in this error message. The logfile tells me the following:
Started GET "/" for 192.168.33.82 at
Thu Apr 07 00:54:48 -0700 2011
ArgumentError (wrong number of
arguments (1 for 0)):
Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/_trace.erb
(1.0ms) Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(4.1ms) Rendered
/usr/lib/ruby/gems/1.8/gems/actionpack-3.0.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb
within rescues/layout (8.4ms)
This means nothing to me really. I have no clue what's going wrong. I currently have only one controller which looks like this:
class SearchEngineController < ApplicationController
def upload
end
def search
#rows = nil
end
# This function will receive the query string from the search form and perform a search on the
# F.I.S.E index to find any matching results
def query
index = Ferret::Index::Index.new :path => "/public/F.I.S.E", :default_field => 'content'
#rows = Array.New
index.search_each "content|title:#{params[:query]}" do |id,score, title|
#rows << {:id => id, :score => score, :title => title}
end
render :search
end
# This function will receive the file uploaded by the user and process it into the
# F.I.S.E for searching on keywords and synonims
def process
index = Ferret::Index::Index.new :path => "public/F.I.S.E", :default_field => 'content'
file = File.open params[:file], "r"
xml = REXML::Document.new file
filename = params[:file]
title = xml.root.elements['//body/title/text()']
content = xml.root.elements['normalize-space(//body)']
index << { :filename => filename, :title => title, :content => content}
file.close
FileUtils.rm file
end
end
The routing of my application has the following setup: Again this is all pretty basic and probably can be done better.
Roularta::Application.routes.draw do
# define all the url paths we support
match '/upload' => 'search_engine#upload', :via => :get
match '/process' => 'search_engine#process', :via => :post
# redirect the root of the application to the search page
root :to => 'search_engine#search'
# redirect all incoming requests to the query view of the search engine
match '/:controller(/:action(/:id))' => 'search_engine#search'
end
If anyone can spot what's wrong and why this application is failing, please let me know. If needed I can edit this awnser and include additional files that might be required to solve this problem.
EDIT
i've managed to get further by renaming one of the functions on the controller. I renamed search into create and now I'm getting back HAML errors. Perhaps I used a keyword...?
woot, finally found the solutions....
Seems I used keywords to define my actions, and Rails didn't like this. This solved issue 2.
Issue 1 got solved by adding Rails.env= 'development' to the environment.rb file

Resources