Default scope Mongomapper - ruby-on-rails-3

I have a big ass collection which uses the same collection which needs to be filter in different ways
class PaymentLog < ActiveRecord::Base
include MongoMapper::Document
set_collection_name "logs"
...
# default scope for payment activity
end
And for example this.
class SuspiciousActivityLog < ActiveRecord::Base
include MongoMapper::Document
set_collection_name "logs"
...
# default scope search for suspicious activity
end
Both use the same logs, but each needs a default search on the type field.

MongoMapper does not support default scope. As explain on the MongoMapper mailing list when hamin wanted to discuss how to add default scopes...
"I personally don't use default scopes. Every time I tried, it ended up biting me." - Brandon Keepers
"I agree with Brandon. I've never had default_scope be useful. It always burns you in the long run. Much better to create a scope/method and always use that method." - John Nunemaker
"I've talked to a few other people and they seem to share your
sentiments John and Brandon. I'll file this one away then as
unnecessary :)" - Haris Amin
If you know that a default scope is the right solution for your problem, you can hack it using MongoMapper's Single Collection Inheritance module as a model:
class PaymentLog
# ...
def self.query(options={})
super.tap { |query| query[:type] = "payment" unless options.key?(:type) }
end
end

I find it quite retarded that MongoMapper does not support default scopes, but only because by default it does not sort documents by anything. SQL databases at least have a incremental id which is naturally used. This is the one reason I believe a default scope is very important.

Related

In a Rails ActiveRecord model, is using after_initialize callbacks a very bad idea?

Let's suppose we have this model
class Account < ActiveRecord::Base
after_initialize :set_name
def set_name
self.name = ‘My Account’
end
end
Now I want run a query that returns only some attributes of the model but not all of them, in particular is not returning the "name" attribute that it is used in after_initialize callback
Account.group(:name).select("count(*), id").first
And then this execution raises the following error because the set_name callback uses an attribute that has not been "loaded" or selected into the records returned by the query.
ActiveModel::MissingAttributeError: missing attribute: name
Fortunately for some particular cases I can execute the same sql query without using the Account model at all to get the desired result
sql = Account.group(:name).select("count(*), id").to_sql
ActiveRecord::Base.connection.execute(sql).first
=> #<Mysql2::Result:0x00000106eddbc0>
But the point is, what if I want to get Account objects instead of a Mysql2::Result one? Should the .select method return "complete" objects with all their attributes (e.g. filling the missing columns with Nil's)? Or is just a very bad idea to use after_initialize callbacks for our ActiveRecord models? Of course we can also add some code in the callback to check if the property exists or not but, in my opinion, this is unnatural or sounds weird working in an OO language.
Most uses of after_initialize can be (and SHOULD be) replaced with defaults on the corresponding database columns. If you're setting the property to a constant value, you may want to look into this as an alternative.
EDIT: if the value isn't constant, a call to has_attribute?(:name) will guard against this error - ActiveModel::MissingAttributeError occurs after deploying and then goes away after a while
No, it is not a bad idea, in fact I use it very often at work. The valid use case for this would be when you want code to run before you try and do anything with the object. Here is a breakdown of some of the filters offered.
# Before you intend to do anything with the object
after_initialize
# Before you intend to save the object
before_save
# After you've saved the object
after_save
# Before you save a new record
before_create
# After you create a new object
after_create

Django determine "most popular"

Given this somewhat simplified representation of my application's models, my question is how do I globally find the most popular MyModel? I.e., those MyModels are favorited the most by MyUsers.
I've come across similar blog posts about how to find favorite tags, but I don't think those apply to this particular situation.
class MyUser(models.Model):
favorite_models = models.ManyToManyField(MyModel)
...
class MyModel(models.Model):
name = models.CharField(...)
...
Can this be done in a single query? Or do I need to loop over every MyUser and MyModel to determine the most popular? Thanks in advance!
I'm too lazy to create a django project from scratch, but this one should do the job:
from django.db.models import Count
MyModel.objects.annotate(Count('myuser'))
(or this)
MyModel.objects.annotate(Count('myuser_set'))
if not, try this:
class MyUser(models.Model):
favorite_models = models.ManyToManyField(MyModel, related_name='myuser')
and then
MyModel.objects.annotate(Count('myuser_set'))
(let me know if it works, in any case this page should contain what you need to do that: https://docs.djangoproject.com/en/dev/topics/db/aggregation/)

Rails 3: Excluding Results by Default

On my site, moderators can flag spammy comments. When these comments are flagged, they get quarantined so they no longer appear in regular views, though they can still be seen in the administrative control panel. At the moment, I exclude them from regular views like so:
#comments = Comment.where(:flagged => false)
I do this in every controller that has comments in it, of which there are many. I get the feeling that there's a cleaner way to handle this in Rails. Perhaps somewhere in the comments model I can specify that when querying for comments, only retrieve those that aren't flagged. If so, how is that done? And even if that's not possible, is there some other way to dry this code?
u can use a default scope
default_scope where(:flagged => false)
see http://apidock.com/rails/ActiveRecord/Base/default_scope/class
the default scope can be ignored using unscoped. See http://apidock.com/rails/ActiveRecord/Base/unscoped/class
i would prefer using a scope rather a default scope since i dont have to override it when all the records are needed. Depends upon the frequency of fetching all/unflagged records.
Make a scope (named 'clean' for this example):
class Comment < ActiveRecord
scope :clean, where(:flagged => false)
end
Then use:
#comments = Comment.clean
For future-proofing, you may may want to add a class method called default_view which just calls clean and use that instead. As your 'default' needs change, just modify the default_view method.

How to get deeply nested errors to get to my REST API?

First, some background:
I have a Company model, a Project model and a Task model. A Project belongs to a company and a Task belongs_to a Project.
The Project model holds several attributes: company_id, date. These attributes uniquely identify a project
I am letting the users create a task by API by POSTing to a URL that contains the details necessary to identify the Project. For example:
POST /projects/<comnpany_name>/<date>/tasks/
In order to make life easier for the users, in case there is no project with the given details, I'd like to create the project on the fly by the given details, and then to create the task and assign it to the project.
...And my problem is:
When there is a problem to create the project, let's say that the company name is not valid, what is the right way to return the error message and communicate to the user?
I'll explain what I mean: I added a create_by_name_and_company_name method to the Project:
def self.create_by_name_and_company_name(name, company_name)
if company = Company.find_by_name(company_name)
project = Project.create(company_id: company.id,
name: name)
else # cannot create this project, trying to communicate the error
project = Project.new(name: name)
project.errors.add(:company, 'must have a valid name')
end
company
end
I was hoping that by returning an unsaved Company object, with errors set, will be a good way communicate the error (This is similar to how rails work when there's a validation error).
The problem is that when calling valid? on the company object, it removed the error I wrote there and adds the regular validation errors (in this case, company can't be blank).
And a bonus question...
And there is a conceptual problem as well: since I'm creating a model by providing parameters that are being used to create the actual attributes, they doesn't always map nicely to the errors[:attr] hash. In this case it is not so bad and I'm using the company field for the company name parameter, but I guess this can get messier when the parameters provided to the create method are less similar to the model attributes.
So what is the preferred approach to tackle that problem? Is there something basically wrong with that approach? if so, what is the preferred approach?
About overriding the default rails validation error message, you need to write your validation constraint like this:
validates_presence_of :name, :message => "must be a valid name"
I figure that it is best to avoid such nesting and stick to a shallower API.

rails 3 default_scope(:where) and find

Find doesn't descope the default_scope anymore, what should I do now? I need to find entries that are out of the default scope on so many places and I also need the scoped arrays of entries for so many lists in my application.
Why did they changed it? :(
Take a look at this article as what has been deprecated in Rails3 here.
So if you want to use the model without the default_scope on it, then you can use the following as in the snippet below.(This is extracted from the article I mentioned)
with_scope and with_exclusive_scope
with_scope and with_exclusive_scope are now implemented on top of Relation as well. Making it possible to use any relation with them :
with_scope(where(:name => 'lifo')) do
...
end
Or even use a named scope :
with_exclusive_scope(Item.red) do
...
end