Rails: list all attributes that can't be mass assigned - ruby-on-rails

I am working through an old legacy project that has recently been updated. The models to the project are in an engine running separate from the controllers and views etc. The engine is running on rails 4.1.6 while the controllers etc are on Rails 3.
This has led to many issues with mass-assignment. I have created a little module that reads the db columns and white lists the attributes for that model. However in a case such as this NewsItem model which has associations and needs to accept attributes for those associations, the module doesn't work.
class Newsitem < ActiveRecord::Base
include MyAttrAccessibleModule
has_and_belongs_to_many :assets
has_and_belongs_to_many :boroughs
has_and_belongs_to_many :venues
I need to add
attr_accessible :asset1_attachment_remove,
:asset1_attachment_title,
:asset2_attachment_title,
:asset3_attachment_title,
:asset4_attachment_title,
:borough_ids,
:venue_ids
But finding all models that require this is a bit of a pain, seeing as there are well over 100.
Is there a way to highlight, find, test, discover in what other models this error might occur in also?

I think what you're looking for could be this:
Object.attributes.keys - Object.accessible_attributes
This should subtract all the whitelisted attributes from all of the available ones.

Thanks to #Stefan Dorunga for his suggestion. It led me in the right direction.
if reflect_on_all_associations(:has_and_belongs_to_many).any?
association = reflect_on_all_associations(:has_and_belongs_to_many)
association.each {|model| attr_accessible model.plural_name.singularize + "_id"}
association.each {|model| attr_accessible model.plural_name}
end
I query the model to see if it has a relationship where the relation id isnt explicitly listed and generate it dynamically.
The only downside is that I have to include the module after all the listed associations. I usually include modules at the top of a class. Whether this is standard practice or not I do not know.

Related

Why Rails 5 uses ApplicationRecord instead of ActiveRecord::Base?

We know that Rails 5 added ApplicationRecord as an abstract class which was inherited by our models (ActiveRecord).
But basically, I think every technical requirement we do with ApplicationRecord, we can also do with ActiveRecord::Base. For instance:
module MyFeatures
def do_something
puts "Doing something"
end
end
class ApplicationRecord < ActiveRecord::Base
include MyFeatures
self.abstract_class = true
end
So now every model will be attached the behaviors of MyFeatures. But we can also achieve this in Rails 4:
ActiveRecord::Base.include(MyFeatures)
So what is the benefit of using ApplicationRecord, do you think it is necessary to add ApplicationRecord?
While it may seem the same in basic Rails applications, there actually is an important difference once you begin to use rails engines, plugins / gems or direct methods from ActiveRecord::Base.
ActiveRecord::Base.include(MyFeatures) mixes in the features directly into ActiveRecord::Base and it is present there forever for all later uses of ActiveRecord::Base (it cannot be "unmixed") and there is no way to get the original ActiveRecord::Base anymore in any code after the include. This can easily lead to problems if some of the mixed in feature changed the default ActiveRecord behavior or if e.g. two engines / gems tried to include same-named methods.
On the other hand, the ApplicationRecord approach makes the features present only for the classes (models) that inherit from it, other classes, as well as direct use of ActiveRecord::Base stay pristine, uncluttered by the module features.
This is especially important when engines or rails plugins are used as it allows them to separate their own model logic from the main application's model logic which was not possible before ApplicationRecord.
All of this is also nicely described in this blog post and this github comment.
This is to expand on #BoraMa's answer, and to, hopefully, clear up some confusion around ActiveRecord::Base.abstract_class.
ActiveRecord::Base.abstract_class goes back to at least Rails 3.2.0 (http://api.rubyonrails.org/v3.2.0/classes/ActiveRecord/Inheritance/ClassMethods.html), which was released on January 20, 2012.
Rails 4.0.0 improved the documentation: http://api.rubyonrails.org/v4.0.0/classes/ActiveRecord/Inheritance/ClassMethods.html
So, to everyone who thinks ApplicationRecord is radically new, it's not. It is an improvement, not a breaking change. Nothing was added to ActiveRecord::Base to make this work.
I did the same thing on a Rails 4.2.6 project because the models used UUIDs for ids instead of integers, and this required a change to the default ORDER BY. So, instead of using copy-paste or a concern, I went with inheritance using a UuidModel class and self.abstract_class = true.

Rails Nested Form and dependent relationships

I currently have a few models, users, clients, addresses and contacts.
I used scaffold to create my client model which only contains name, leadsource and DBA. It is currently working just fine.
My address and contact was created separately as a client can have many addresses and contacts and they require a client_id to set up the relationship.
Assuming I am using your standard crud operations, is there any way to set up a single nested form that will allow me to add all 3 at once?
Sure, the standard way is fields_for.
But normally you also want to dynamically add and remove associated objects (i.e. addresses) and that's a bit more advanced.
There are gems to help you. i.e. cocoon and a great railscast: episode 403.
Unfortunately it's a pro episode, but investing the $9 is well worth it, as the railscasts really are a source of inspiration (at least for me).
I assume, you already have the associations or create them:
rails generate migration AddClientReferenceToAddress client:references
rake db:migrate
and in models:
class Client < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :client
end

How do I avoid using Concerns when designing Rails database schemas?

I read many blogs, and one of the themes that comes across often is that concerns (at least the way Rails defines them) are damaging to software. On balance I agree - simply including behaviour into models is violating the single responsibility principle. You end up with a god-class that does too much.
But as with many of the opinions gleaned from blogs, an alternative architecture is rarely provided.
So let's take an example app, loosely based on one I have to maintain. It's inherently a CMS, as many Rails apps tend to be.
Currently each model has a large number of concerns. Let's use a few here:
class Article < ActiveRecord::Base
include Concerns::Commentable
include Concerns::Flaggable
include Concerns::Publishable
include Concerns::Sluggable
...
end
You can imagine that 'Commentable' would require only a small amount of code added to the Article. Enough to establish relationships with comment objects and provide some utility methods to access them.
Flaggable, allowing users to flag inappropriate content, ends up adding some fields to the model: flagged, flagged_by, flagged_at for example. And some code to add functionality.
Sluggable adds a slug field for referencing in URLs. And some more code.
Publishable adds publish date and status fields, with yet more code.
Now what happens if we add a new kind of content?
class Album < ActiveRecord::Base
include Concerns::Flaggable
include Concerns::Sluggable
include Concerns::Publishable
...
end
Albums are a bit different. You can't comment on them, but you can still publish them and flag them.
Then I add some more content types: Events and Profiles, let's say.
I see a few problems with this architecture as it stands:
We have multiple database tables with exactly the same fields
(flagged_by, published_on etc.)
We can't retrieve multiple content types at once with a single SQL query.
Each model supports the duplicated field names with the included concerns, giving each class multiple responsibilities.
So what's a better way?
I've seen decorators promoted as a way to add functionality at the point where it's needed. This could help solve the issue of included code, but the database structure isn't necessarily improved. It also looks needlessly fiddly and involves adding extra loops to the code to decorate arrays of models.
So far my thinking goes like this:
Create a common 'content' model (and table):
class Content < ActiveRecord::Base
end
The associated table is probably quite small. It should probably have some kind of 'type' field, and maybe some things common to absolutely all content - like a type slug for URLs perhaps.
Then rather than adding concerns we can create an associated model for each behaviour:
class Slug < ActiveRecord::Base
belongs_to :content
...
end
class Flag < ActiveRecord::Base
belongs_to :content
...
end
class Publishing < ActiveRecord::Base
belongs_to :content
...
end
class Album < ActiveRecord::Base
belongs_to :content
...
end
...
Each of these is associated with one piece of content, so the foreign key can exist on the feature's model. All the behaviour relating to the feature can also exist solely on the feature's model, making OO purists happier.
In order to achieve the kind of behaviour that usually requires model hooks (before_create for example) I can see an observer pattern being more useful. (A slug is created once a 'content_created' event is sent, etc.)
This looks like it would clean things up no end. I can now search all content with a single query, I don't have duplicated field names in the database and I don't need to include code into the content model.
Before I merrily unleash it on my next project, has anyone tried this approach? Would it work? Or would splitting things up this much end up creating a hell of SQL queries, joins and tangled code? Can you suggest a better alternative?
Concerns are basically just a thin wrapper around the mixin pattern. It is a very useful pattern for composing pieces of software around reusable traits.
Single Table Inheritance
The issue of having the same columns across several models is often solved with Single Table Inheritance. STI however is only really suited when the models are very similar.
So lets consider your CMS example. We have several different types of content:
Page, NewsArticle, BlogPost, Gallery
Which have pretty much identical database fields:
id
title
content
timestamps
published_at
published_by
# ...
So we decide to get rid of duplication and use a common table. It would be tempting to call it contents but that is extremely ambiguous - content of the content ...
So lets copy Drupal and call our common type Node.
class Node < ActiveRecord::Base
include Concerns::Publishable
end
But we want to have different logic for each type of content. So we make subclasses for each type:
class Node < ActiveRecord::Base
self.inheritance_column = :type
include Concerns::Publishable
end
class NewsArticle < Node
has_and_belongs_to_many :agencies
end
class Gallery < Node
has_and_belongs_to_many :photos
end
# ...
This works well until the STI models start to diverge for too much from each other. Then some duplication in the database schema can be a far smaller problem than the massive complications caused by trying to shoehorn everything into the same table. Most CMS systems built on relational databases struggle with this issue. One solution is to use a schemaless non-relational database.
Composing concerns
There is nothing in that says that concerns require you to store on the models table. Lets look at several of the concerns you have listed:
Flaggable
Sluggable
Commentable
Each of these would use a table flags, slugs, comments. The key is making the relation to object that they flag, slug or comment polymorphic.
comment:
commented_type: string
commented_id: int
slugs:
slugged_type: string
slugged_id: int
flags:
flagged_type: string
flagged_id: int
# ...
class Comment
belongs_to: :commented, polymorphic: true
end
module Concerns::Commentable
# ...
has_many: :comments
end
I would suggest that you look at some of the libraries that solve these kind of common tasks such as FriendlyId, ActsAsTaggedOn etc to see how they are structured.
Conclusion
There is nothing fundamentally wrong with the idea of parallel inheritance. And the idea that you should forgo it just to placate some kind extreme OO purity ideal is ridiculous.
Traits are a part of object orientation just any other composition technique. Concerns are however not the magic-fix all that many blog posts would have you believe.

Is there a (code-design) reason to not embed classes when designing MongoMapper models?

This question relates to using Ruby on Rails 3 with MongoMapper and EmbeddedDocument. Also, the ManyAssociation.
http://mongomapper.com/documentation/embedded-document.html
The MongoMapper examples (in the link above) show two separate classes:
class Order
include MongoMapper::Document
many :line_items
timestamps!
end
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
But this pollutes the global namespace with LineItem. Out of context, LineItem for what? And what if I want another model, say WishList, to also have a LineItem set?
So it is possible to embed the LineItem class inside Order, like this:
class Order
include MongoMapper::Document
many :line_items, :class_name => "Order::LineItem"
timestamps!
class LineItem
include MongoMapper::EmbeddedDocument
key :name, String
key :quantity, Integer
end
end
While this may be technically fine (yes?), will I run into design issues later on? Does it just make the code too ugly? Too complex?
Presumably, the existence of this in the Ruby language means someone thinks it's an ok idea?
One thing I always liked about Django is how it uses "apps" to group related model classes (and separate the namespaces). So my code above is also achieving that in Rails.
I don't see any technical problems to either approach. The problem I've run into is that when a class is embedded in it's parent and is also in the same file, I'll forget it's there. So if your namespaces it as Order::LineItem you can make an "order" folder in your "models" folder and put "line_item.rb" in there.
The other trouble is if you want to have a controller for Order::LineItem, you also have to namespace it and put it in a folder, and in the router it will look like:
resource :orders do
resources :line_items, :controller => "order/line_items"
end
Unless you know your app is going to have multiple types of line_items, I'd recommend not namespacing it—you could be over-coding if you did. For example, if you were later to need two types of line_items you might even find that some of your code could be re-used between models—and if you namespaced your first line_item you may find yourself de-namespacing it.
In Python, namespaces are considered a great thing that there should be more of. But when you think about the global namespace of your Rails app, it's not that cluttered. Gem authors are pretty good about keeping all of their classes namespaced in their one gem module, so in your Rails app you're going to have one namespace for each gem you're using (Rails itself is 5 gems, not sure how many global constants though), plus and a bunch of files included by Rails (e.g. SecureRandom). It turns out to be really nice to have those files "just there" and I've found in practice that namespace collisions are rare and you can easily work around them. I've only run into a namespace issue maybe once, but several times I've accidentally defined a "send" method on a model—a much more common problem with similar repercussions.

Rails class-table inheritance gem, faux-inheritance, and missing methods

I'm working on modifying part of an existing Rails app to use the Class-Table-Inheritance gem (https://github.com/brunofrank/class-table-inheritance). All's well, except that I have defined some instance methods in my superclass -- Person, which all subclasses need to be able to access. For instance, full_name, which returns the concatenated first and last names, or cite_name, which returns the first initial and last name. Since the CTI gem doesn't actually use Ruby inheritance (all subclasses still inherit from ActiveRecord::Base, and the gem does some funky voodoo to link the ActiveRecord fields together), I can't access these methods in the subclasses I've created. Any thoughts on working around this? I'm not interested in STI, but I'm willing to either fork and hack on this particular CTI gem, or look at other solutions.
try http://peterhamilton.github.com/citier, it's based on CITIEsForRAILS, I forked it & put it through rapid development and various other changes. It fixes most, if not all the bugs it had and works so simply.
Just off the top of my head, did you see that in the migration tables of your subclasses you need to have the line:
create_table :videos, :inherits => :product do |t|
and also in your Model.rb files
class Product < ActiveRecord::Base
acts_as_superclass # I'm guessing you might be missing this line??
end
class Book < ActiveRecord::Base
inherits_from :product
end
I think that the brand new CITIEsForRAILS gem (Class Inheritance & Table Inheritance EmbeddingS For RAILS, see https://github.com/altrabio/CITIEsForRAILS ) does exactly what you desire. This gem extends CTI, STI, & Multi Table Inheritance while preserving Ruby class Inheritance.

Resources