2

I have a dead simple rails application w/ rails4 and mongoid. I can create new datasets like a charm. But I just can't update existing datasets.

Anyone has this issue? How does this work, what am I doing so wrong?

Just created from scratch with rails 4, ruby 2 and mongoid, all from their git repos:

rails new mongotest --skip-active-record

I generatet a scaffold:

rails g scaffold things name description

My model now looks like this:

class Thing
  include Mongoid::Document
  field :name, type: String
  field :description, type: String
end

Controller like that:

class ThingsController < ApplicationController
  before_action :set_thing, only: [:show, :edit, :update, :destroy]

  # GET /things
  # GET /things.json
  def index
    @things = Thing.all
  end

  # GET /things/1
  # GET /things/1.json
  def show
  end

  # GET /things/new
  def new
    @thing = Thing.new
  end

  # GET /things/1/edit
  def edit
  end

  # POST /things
  # POST /things.json
  def create
    @thing = Thing.new(thing_params)

    respond_to do |format|
      if @thing.save
        format.html { redirect_to @thing, notice: 'Thing was successfully created.' }
        format.json { render action: 'show', status: :created, location: @thing }
      else
        format.html { render action: 'new' }
        format.json { render json: @thing.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /things/1
  # PATCH/PUT /things/1.json
  def update
    respond_to do |format|
      if @thing.update(thing_params)
        format.html { redirect_to @thing, notice: 'Thing was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @thing.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /things/1
  # DELETE /things/1.json
  def destroy
    @thing.destroy
    respond_to do |format|
      format.html { redirect_to things_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_thing
      @thing = Thing.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def thing_params
      params.require(:thing).permit(:name, :description)
    end
end

My Gemfile:

source 'https://rubygems.org'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails',     git: 'https://github.com/rails/rails.git'
gem 'arel',      git: 'https://github.com/rails/arel.git'
gem 'activerecord-deprecated_finders', git: 'https://github.com/rails/activerecord-deprecated_finders.git'

gem 'thin'
gem 'mongoid', git: 'https://github.com/mongoid/mongoid.git'



# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '~> 4.0.0.beta1'
  gem 'coffee-rails', '~> 4.0.0.beta1'

  # See https://github.com/sstephenson/execjs#readme for more supported runtimes
  # gem 'therubyracer', platforms: :ruby

  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'

# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'

# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.0.1'

And this is from the logs when trying to update a dataset:

Started PATCH "/things/51669078e05658cf22000001" for 127.0.0.1 at 2013-04-11 12:29:25 +0200
Processing by ThingsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"lfELkffFSf9gk04TnHnzG9cCrOe3XrsjK4fEZ7Rt7qQ=", "thing"=>{"name"=>"qqqq", "description"=>"qqqqq"}, "commit"=>"Update Thing", "id"=>"51669078e05658cf22000001"}
  MOPED: 127.0.0.1:27017 QUERY        database=mongotest_development collection=things selector={"_id"=>"51669078e05658cf22000001"} flags=[:slave_ok] limit=0 skip=0 batch_size=nil fields=nil (0.5772ms)
Redirected to http://localhost:3000/things/51669078e05658cf22000001
Completed 302 Found in 6ms

If anyone has any hints or a link ... Help is very much appreciated.

Update 1: I did try that in a rails 3 environment and it works ... well exactly like expected.

Update 2: Logger output with "Mongoid.logger.level = Logger::DEBUG" and "Moped.logger.level = Logger::DEBUG":

Started PATCH "/things/5166c5ece05658f08a000001" for 127.0.0.1 at 2013-04-11 16:20:56 +0200
Processing by ThingsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"lfELkffFSf9gk04TnHnzG9cCrOe3XrsjK4fEZ7Rt7qQ=", "thing"=>{"name"=>"EditTest", "description"=>"123"}, "commit"=>"Update Thing", "id"=>"5166c5ece05658f08a000001"}
D, [2013-04-11T16:20:56.450464 #37961] DEBUG -- :   MOPED: 127.0.0.1:27017 QUERY        database=mongotest_development collection=things selector={"_id"=>"5166c5ece05658f08a000001"} flags=[:slave_ok] limit=0 skip=0 batch_size=nil fields=nil (0.7041ms)
Redirected to http://localhost:3000/things/5166c5ece05658f08a000001
Completed 302 Found in 6ms

Update 3: The mongoid.yml I used for last log output:

development:
  # Configure available database sessions. (required)
  sessions:
    # Defines the default session. (required)
    default:
      # Defines the name of the default database that Mongoid can connect to.
      # (required).
      database: mongotest_development
      # Provides the hosts the default session can connect to. Must be an array
      # of host:port pairs. (required)
      hosts:
        - localhost:27017
      options:
        # Change whether the session persists in safe mode by default.
        # (default: false)
        safe: true

My example app is at https://github.com/jlxq0/mongotest

Update 4: More research showed that this /could/ be a similar problem like Rails 4.0.0.beta 1 and Mongoid

If noone has an answer I'd also be happy if someone shared a link to the sources of a working rails4 / Mongo example so I can figure out differences by myself.

Update 5: In Rails console, update works just fine.

Community
  • 1
  • 1
jlxq0
  • 87
  • 1
  • 8
  • Use safe mode on development to see the errors on MongoDB in your mongoid config file and post back the error you get. – gmaliar Apr 11 '13 at 14:11
  • I configured logging like explained on [Mongoid Docs](http://mongoid.org/en/mongoid/docs/installation.html#logging) Output (too long for comment, will post in next comment) down't tell me anything. – jlxq0 Apr 11 '13 at 14:18

3 Answers3

8

This is because update does not take attributes, it only accepts options, like @thing.update(validate: false).

In order for your code to work, you can do the following:

if @thing.update_attributes(thing_params)
  #...
end

Or:

@thing.attributes = thing_params
if @thing.save
  #...
end

Or:

@thing.attributes = thing_params
if @thing.update
  #...
end
modetojoy
  • 166
  • 1
0

MongoDB has something called a safe mode and it is off by default, this means that whatever you tell it to execute it will return OK for it. If you set safe mode to on, if you execute a query that is illegal by mongo standards it will raise an exception.

On development what you would usually do is this,

development:
  sessions:
    default:
      hosts:
        - localhost:27017
      database: mongoid
      username: user
      password: password
      options:
        safe: true

after that you can post us your exception and we can try to solve it!

gmaliar
  • 5,294
  • 1
  • 28
  • 36
  • Guy, thank you for your hint. I tried that, but to be honest, I don't really see anything unusual there. I will try to share the whole solution on GitHub. – jlxq0 Apr 11 '13 at 14:26
  • Also try using `save!` instead of `save`, to see the exceptions – gmaliar Apr 11 '13 at 14:29
  • I only have `save` in create action, in my controller. Create works fine. For update action it's `@thing.update(thing_params)`. I think, there is no such thing as `update!`. I've added my full sources to [this Github Repo](https://github.com/jlxq0/mongotest) – jlxq0 Apr 11 '13 at 14:35
0

In the case that you're working with embedded attributes (whether a dynamically created Hash, or an explicitly defined relationship), using upsert instead of update may be of assistance.

Upsert results in the field being re-written if the entry exists, or created if it doesn't.

i.e.

person = Person.new(
  first_name: "Heinrich",
  last_name: "Heine"
)
person.upsert

Example sourced from: http://mongoid.org/en/mongoid/docs/persistence.html

XtraSimplicity
  • 5,704
  • 1
  • 28
  • 28