mongoid / mongoid-history

Multi-user non-linear history tracking, auditing, undo, redo for mongoid.
https://rubygems.org/gems/mongoid-history
MIT License
393 stars 129 forks source link

NoMethodError: undefined method `where' for HistoryTracker:Class #169

Open prasadsurase opened 8 years ago

prasadsurase commented 8 years ago

I am using mongoid-history(0.5.0) with an rails(5.0.0) and mongoid(github master branch). The changes are

#app/models/history_tracker.rb
class HistoryTracker
  include Mongoid::History::Trackable
end

#config/initializers/history_tracker.rb
Mongoid::History.tracker_class_name = :history_tracker

#app/models/user.rb
class User
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::History::Trackable
  include Mongoid::Attributes::Dynamic

  ...
  field :name, type: String
  field :points, type: Integer, default: 0
  ...

  track_history on: [:name, :points], track_create: false, track_update: true, track_destroy: true
end

When I run 'user.history_tracks' in rails console, I get

2.3.0 :012 > user = User.last
 => #<User _id: 10de0dd7710542, created_at: 2016-09-13 15:10:07 UTC, updated_at: 2016-09-13 15:10:07 UTC, email: "prasad@gmail.com", encrypted_password: "$2a$10$ByExx918bnV6", remember_created_at: nil, sign_in_count: 1, current_sign_in_at: 2016-09-13 15:10:07 UTC, last_sign_in_at: 2016-09-13 15:10:07 UTC, current_sign_in_ip: "::1", last_sign_in_ip: "::1", github_handle: "prasad", active: true, name: "Prasad Surase", provider: "github", uid: "56", avatar_url: "https://avatars.githubusercontent.com/u/56?v=3", points: 0, version: nil, modifier_id: nil> 
2.3.0 :013 > user.version
 => nil 
2.3.0 :014 > user.modifier_id
 => nil 
2.3.0 :015 > user.history_trac
user.history_trackable_options  user.history_tracks             
2.3.0 :015 > user.history_tracks
NoMethodError: undefined method `where' for HistoryTracker:Class
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/mongoid-history-0.5.0/lib/mongoid/history/trackable.rb:75:in `history_tracks'
    from (irb):15
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/railties-4.2.5/lib/rails/commands/console.rb:110:in `start'
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/railties-4.2.5/lib/rails/commands/console.rb:9:in `start'
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:68:in `console'
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/railties-4.2.5/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /Users/prasad/.rvm/gems/ruby-2.3.0/gems/railties-4.2.5/lib/rails/commands.rb:17:in `<top (required)>'
    from ./bin/rails:4:in `require'
    from ./bin/rails:4:in `<main>'

Am I missing something or is this a valid bug?

dblock commented 8 years ago

I released 0.6.0 an hour ago, can you please try that?

prasadsurase commented 8 years ago

@dblock I created sample app using ruby 2.3.0, rails 5.0.0, mongoid(github master branch) and mongoid(0.6.0).

1) Can't create a new User because of validation errors.

2.3.0 :014 >   user = User.create(email: 'prasad@prasad.com', password: 'prasad123', password_confirmation: 'prasad123')
 => #<User _id: BSON::ObjectId('57d8e68b5f10de200053b8f6'), created_at: nil, email: "prasad@prasad.com", modifier_id: nil, name: nil, points: 0, updated_at: nil, version: nil> 
2.3.0 :015 > user.valid?
 => false 
2.3.0 :016 > user.errors
 => #<ActiveModel::Errors:0x007f9af9def670 @base=#<User _id: BSON::ObjectId('57d8e68b5f10de200053b8f6'), created_at: nil, email: "prasad@prasad.com", modifier_id: nil, name: nil, points: 0, updated_at: nil, version: nil>, @messages={:modifier=>["can't be blank"]}, @details={:modifier=>[{:error=>:blank}]}>

Also, I tried using the latest version(0.6.0) for an existing open-source project and it results in the same error( NoMethodError: undefined method `where' for HistoryTracker:Class). This is the commit where I have tried to integrate mongoid-history. Please feel free to clone the repo to check locally.

sivagollapalli commented 8 years ago

@dblock This issue has been raised because of belongs_to_required_by_default has been set to true once we add mongoid-history gem in Gemfile actually it should not. I have gone through code but I didn't find anywhere where we are overriding mongoid options.

dblock commented 8 years ago

I would try to turn it into a failing spec here next.

prasadsurase commented 8 years ago

@dblock @sivagollapalli that might be the case but the app link that I have provided in the latest comment runs on rails 4.2.

prasadsurase commented 8 years ago

for further explanation,

2.3.0 :003 > HistoryTracker.ancestors
 => [HistoryTracker, Mongoid::History::Trackable, Object, PP::ObjectMixin, EasyDiff::SafeDup, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object, BSON::Object, Origin::Extensions::Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject] 

expect for Mongoid::History::Trackable and Mongoid::Extensions::Object no other Mongoid classes/modules are present in the chain which seems weird.

Ninigi commented 7 years ago

Not entirely sure if this is related, but I'll just throw it out there.

I've had an issue with Rails 5.0.1, Mongoid 6.0.2 and Mongoid-History 0.6.0. The app acts as a standalone API and shares its database with a Rails 4 app. The Rails 5 app is read-only and I did not have a problem with it up until today when I opened a rails console and tried to update a record, which always returned false. Even without calling save or validate every record had errors and was invalid right from when loaded. When I inspected the errors I got

record.errors.messages
#=> :modifier=>["can't be blank"]

So, after a little research (with a short stop in this thread) I ended up reading the mongoid config specs and realised they are setting Mongoid.belongs_to_required_by_default = true by default, which is in compliance with the rails 5 active record default setting (changed from default = false to default = true), but screwed up everything when not saving a modifier.

Simply adding an initializer solved the problem:

# config/initializers/mongoid_belongs_to_required_by_default.rb

Mongoid.belongs_to_required_by_default = false
dblock commented 7 years ago

It sounds like a separate (and real) problem @Ninigi.

Ninigi commented 7 years ago

@dblock so, should I open an issue? The problem is an unexpected behaviour because of the Mongoid default setting, a simple comment about it in the README would already fix it (since you only need to add the initializer)

dblock commented 7 years ago

I think you should and maybe try to write a spec for it? At the very least it's a bug, and your solution is a workaround, but we should actually fix the underlying issue.

Ninigi commented 7 years ago

Ok, will do. I think the solution is going to be easy enough, so probably will have a PR attached. Thanks for your time :)

DarthHater commented 7 years ago

@Ninigi you just saved my butt on a project, thanks for that.

Ninigi commented 7 years ago

@DarthHater thanks for the PR, I wanted to do it and never found the time... And then I forgot >.<

dblock commented 7 years ago

What do we do with this issue here? Looks like Mongoid.belongs_to_required_by_default was required? Not sure I understand what's going on.

Ninigi commented 7 years ago

@dblock The issue basically is that ActiveRecord changed its policy towards relations with Rails 5. Before, when you defined a belongs_to relation, the foreign key was not required (could be null or not), now it is required by default unless stated otherwise.

Mongoid adopted that behavior, which breaks mongoid-history if you try to save a history without setting the modifier_id (belongs_to relation), because modifier_id will now be validated for presence by default - hence the :modifier => "cant be blank" error message.

It is not related to the original issue I think, but rather to the issue that prasadsurase ran into when creating a sample app:

2.3.0 :014 >   user = User.create(email: 'prasad@prasad.com', password: 'prasad123', password_confirmation: 'prasad123')
 => #<User _id: BSON::ObjectId('57d8e68b5f10de200053b8f6'), created_at: nil, email: "prasad@prasad.com", modifier_id: nil, name: nil, points: 0, updated_at: nil, version: nil> 
2.3.0 :015 > user.valid?
 => false 
2.3.0 :016 > user.errors
 => #<ActiveModel::Errors:0x007f9af9def670 @base=#<User _id: BSON::ObjectId('57d8e68b5f10de200053b8f6'), created_at: nil, email: "prasad@prasad.com", modifier_id: nil, name: nil, points: 0, updated_at: nil, version: nil>, @messages={:modifier=>["can't be blank"]}, @details={:modifier=>[{:error=>:blank}]}>
dblock commented 7 years ago

Okay, so I think what we need is either or both documentation that explains this in README and much better error messaging. Care to contribute @Ninigi?

jkras commented 7 years ago

Would love to help, though kind of new to open source.

I'm facing the same issue - switching belongs_to_required_by_default off allows me to crud my models but obviously doesn't track the user that did it as there is nothing forcing MH to write the modifier.

When I don't turn it off, I simply get an :modifier=>["can't be blank"] error.

Can't establish if I should set the modifier manually (doesn't seem like the correct approach), or how to debug this and have mongoid history set the modifier to the current_user

Additionally, I'm running rails 5.0.2, mongoid 6.1.0, mongoid-history 0.6.1, devise 4.2.1 (also running cancancan)...

Thanks!

jkras commented 7 years ago

I have a solution to my question. Posting it for feedback, or in case anyone else has the problem ...

In #139 @dblock mentions that you need to set the modifier explicitly. Seeing as though there is one model at the moment that I want to track changes in, I've added a merge to the strong params method which now looks like this:

def object_params
      params.require(:object).permit(:field1, :field2).merge(modifier: current_user)
end

This works, so I'll keep it like this for now, unless anyone has a better implementation idea

ErvalhouS commented 7 years ago

Issue for me was that creating through console the modifier_id needs to be passed manually or as some id from seed or as default value like, example:

  field :modifier_id, type: String, default: -> { self._id }
alexismansilla commented 7 years ago

thanks for that @Ninigi