I have a model ProductFeature
a controller called product_feature_controller.rb
I am writing the 'edit' method of my CRUD for this controller. In the edit method, I created an instance variable named #productFeature and set it equal to ProductFeature.find(:id) and I send this instance variable to my view to be used by a form_for(:product_feature). I have found that my form will not populate its fields with the current data from the record I am pulling up. After pulling hairs for some hours, it dawned on me that my error was all in the way named my instance variable.
It should be named #product_feature instead of #productFeature. This seems like a trivial detail, but I'd really like to know why this is the case. How does this even work? I mean how does Rails know to populate my form based on the way the instance Variable is named? I hope my question is clear, just seeking further edification.
Because the symbol :product_feature (in the form_for) is going to look up an instance variable based on that symbol. It doesn't do any transformation, and will be looking for a #product_feature variable. As long as they match, you should be okay, but the Ruby world uses underscores between words.
Also, models should be named with a leading capital, ProductFeature, by convention. I don't know if this will cause any issues, but it's counter to the norm.
Related
I would like to make sure my code is properly organized/designed according to the following paradigms/patterns:
- Model View Controller
- Rails Convention over Configuration
- Skinny Controller, Fat Model
I have listed them here in the order I think is most important.
My issue
After reading several articles, in particular this one and this one too, I started to move some of the logic in my controllers, to my models.
However, I can't decide whether or not to move my searching logic (explained in depth, later) from the controller to the model, even after reading more posts/articles like these: MVC Thinking, and Model vs. Controller, Separating concerns and many more not listed here.
The scenario
View
Two pages:
1 page contains a text field and submit button which sends the user's input as an argument in params in a POST request to the second page.
The second page simply renders each neatObject in a given array, let's call it #coolList.
Controller
A method, let's call it awesomeSearch, is invoked when the second page receives a POST request. (Thanks to Rails Routing)
awesomeSearch should take the user's input, available as params[:searchString], and work with the NeatObject model to build the #coolList and make that list available for the view to render.
Model
The NeatObject model handles requests from controllers and returns neatObjects back to those controllers.
The NeatObject model defines the relationship between neatObjects and other tables in our database.
Database
These are the attributes that make up each neatObject according to our database:
id - int
description - string
address - string
date_created - timestamp
What's Missing?
How the controller works with the model to get matches for the user's input.
This is the part I am confused about. The logic itself is very simple, but I am not sure which pieces belong in the model, and which pieces belong in the controller.
Should the controller pass the search string to the model, and the model pass back the results?
Should the controller ask the model for all of the neatObjects, then only keep the ones that match?
Is the solution a little of both?
To be able to ask questions about specific bits of the logic, I'll next outline the search process with more detail.
The search logic in depth
The process involves finding neatObjects that match the search string. It would be impossible to move on without defining what we consider matches for neatObjects. To keep things very simple, we will say that:
A neatObject matches a search string if the search string is contained within its description or its address, ignoring case and leading/trailing whitespace.
This definition is not guaranteed to be permanent. There are several things that may change our definition. We may need to test against more attributes than just address and description, perhaps if the database guys add a new important attribute, or the UI guys decide users should be able to search by ID. And of course, the opposite of these scenarios would mean we need to remove an attribute from the list of attributes we are testing. There are many situations that could change our definition of a match. We could even have to add or remove logic, perhaps if it is decided that we should only test the first word in the description attribute, or perhaps if we should no longer ignore case.
Now we know what defines a match and we know that our definition may change. Now we can more concretely define the search process.
Here is an outline of the steps:
Get a reference to all neatObjects
Loop through each neatObject, putting each individual one through the match test
Test passes - add/keep neatObject in results list
Test fails - do not keep neatObject for results
Make results available to the view
I will reference these steps as I show possible implementations.
Implementation
The search functionality can easily be implemented in either the NeatObject model, or the controller serving the view.
Normally, I would just write all of the logic in the controller, but after I learned about the "Skinny controller, Fat model" design, I thought it definitely applied in this situation. This article in particular made me think to reorganize my code, after I saw the author had implemented a "search-like" function in the model. The author's function did not handle user input though, so I was wondering how it should be handled.
This is how I would have written the code before learning about "SCFM":
Search logic in controller:
#searches_controller.rb
#This is the method invoked when second page receives POST request
def search
#neatObjects = NeatObjects.all.to_a
#neatObjects.delete_if {
|neatObject| !matches?(neatObject, params[:searchString])
}
end
def matches?(neatObject, searchString)
if((neatObject.description.downcase.include? searchString.downcase) ||
(neatObject.address.downcase.include? searchString.downcase))
return true
end
return false
end
This method gets its reference to all of the neatObjects (Step 1) by calling .all() on the NeatObject model. It uses the array function delete_if to perform the match test on each neatObject, and only keeps those that pass (Step 2). Step 3 is accomplished automatically automatically since we store our results in an instance variable in the controller which serves the view.
Placing the logic in the controller is very straight-forward, but when considering the "SCFM" design pattern, it seems very illogical.
I've written another option, in which the controller sends the user's input to a function in the model, which will return the the neatObjects which match the input.
Search logic in Model
#NeatObject.rb
def self.get_matches_for(searchString)
all.to_a.delete_if { |neighborhood| !matches?(searchString, neighborhood) }
end
def self.matches?(phrase, neighborhood)
fields = [neighborhood.name, neighborhood.address]
fields.map!(&:downcase)
phrase.downcase!
fields.each do |field|
if (
(phrase.include? field) ||
(field.include? phrase)
)
return true
end
end
return false
end
This method gets the complete list of neatObjects with all() (Step 1). Just like the first method, the model method uses delete_if to remove array elements (neatObjects) that don't meet a certain criteria (passing the match test) (Step 2). In this method, the controller serving the view would call get_matches_for on the NeatObject model, and store the results in an instance variable (Step 3), like this: #neatObjects = NeatObject.get_matches_for( params[:searchString] )
I do think that the model option is cleaner, and slightly more maintanable, but I'll go more in depth in the following section.
Concerns
I can see pros and cons to both the model method and the controller method, but there are things that I'm still unsure about.
When I read over that article I've referenced several times (just like I did here), it was very logical that the model defined a function to return the recently added people.
The controller didn't have to implement the logic to determine if a person had been recently added. It makes sense that the controller shouldn't, because that is dependent on the data itself. There might be a completely different implementation of the "recency" test for messages. The recent people might include people added this week, while recent messages are only those messages sent today.
A controller should just be able to say People.find_recent or Message.find_recent and know it got the correct results.
Is it correct to say the find_recent method could also be modified to take in a time symbol, and return objects for different time periods? Ex - People.find_in_time( :before_this_month ) or Messages.find_in_time( :last_year ). Does that still follow the MVC pattern and Rails convention?
Should a controller be able to find matches for user input with NeatObject.get_matches_for( searchString )?'
I think the matching logic belongs in the model, because there are certain/specific attributes that are used in testing, and those attributes are different depending on the data. We might have different attributes for different tables, and those attributes definitely shouldn't be defined by the controller. I know the controller depends on the model, not the other way around, so the model must define those attributes, even if the rest of the logic is in the controller.
And if the model defines the attributes that are used when searching, why shouldn't it define the entire search function?
The above text explains why I think the model should handle the searching logic and expresses the majority of my questions/concerns, however I do have some opinions favoring the other option.
If the model is concerned only with the data, how can one justify passing user input to it?
If the controller doesn't handle the search logic, it still needs to send the user's input to the model. Does it clean it up before it sends it? (Removing leading/trailing whitespace and downcasing the string.) Does it just send the exact input it got from the user?
Where is the testing done to make sure the input is not malicious?
Some of my biggest questions:
Does the answer for where the logic goes change for each case?
If the search/matching process was simpler, would the code's place change? For example, if the searching was simpler:
If the only attribute being tested was the address and that was unlikely to change, would we just handle the search with the controller?
If we were making an advanced search feature, where the user decided which attributes to include in the search and also controlled some other factors, there would be a lot of user input just to define the arguments to the search function. Would that be too much logic or user input to place in the model?
In conclusion
How can I always be sure that I am putting logic in the right place (MVC)?
For my specific case, with this data and this search/match process, how should I organize the code?
What guidelines can be followed when you find a gray area, or are unsure about where logic belongs?
This is something that varies a lot between cases (each application is different) but here's what I do on my end (large sports app that searches lots of tables from many different places):
start by performing small searches with non-repeating code patterns in the controller (or rarely view)
when it turns out the code is needed in more than one or two action/view I move it to the models
I also move the code to the model when the query complexity goes above the average .find or simple .where
Like this, you don't spam your model with one-time-use methods, and at the same time don't repeat the same code over multiple controllers/actions/views
As many thinks in IT and in life, It depends of the scale and the goal.
Considerations:
1) For design: If you see you are violating DRY in your controllers many times, its probably time to move your logic to models.
2) For performance: Since controllers are loaded more than models, Logic in controller tend to performs worst.
And not less important: Unless you are doing something very trivial, and you db has a few thousands rows, do NOT use db for text search. Instead, use a search engine like Solr, ElasticSearch, Sphinx, etc.
I am debugging some Groovy code for a website and have hit an issue where I create an object A in controller in one part of the flow and set a variable within it (read it back and its correct).
I then pick up what I had understood to the the same object in a different controller. But the variable is no longer set.
Either my assumption that the object A in the first controller is the same object A as picked up the the second controller is wrong or something has modified the value en-route.
So, what might be a very basic question (and I have have a horrible feeling that it points to some fundamental misunderstanding on my part of how Groovy/Java works - so please be gentle) :
How can I tell if the object A in controller 1 is the same as the object A in controller 2 (by the same I mean point to the same object, not that they are equivalent).
I then pick up what I had understood to the the same object in a
different controller.
If you show an example of what you are doing in the first controller and what you are doing in the second controller that would help clarify what is going on. It isn't clear what you might mean by "pick up" in the sentence quoted above.
If you can orchestrate things such that you have the 2 references at the same time you can call o1.is(o2) which will tell you if o1 points to the same object as o2. Something you can use to help debug the situation is in the first controller your can call System.identityHashCode(o1) and in the second controller you can call System.identityHashCode(o2) and see if those return the same value.
There are times in a web app where the notion of being the same object can be ambiguous. For example, if you have 2 separate proxies but they are proxying the same instance, there are contexts where you would treat them as the same object. Another example is that if you are dealing with persistent entities you could have 2 separate instances in memory that actually correspond to the same record in the data store.
Anyway, the identityHashCode approach mentioned above is a technique you could use to know if these objects are the same object or not. If that doesn't do it for you and you can show some code or provide some more details that might help.
If you want to make some variables available in several controllers, you can do it in one of the following ways:
put the object in a session
put the object in a flash scope
put the object in a flow scope - here you MUST make sure, that you are accessing the SAME flow.
use a singleton service to persist the value permanently or temporarily
use a static field somewhere (shall never be used though)
I am introducing a soft-deletion pattern in my app. Instead of deleting objects from my context, I will be flagging them as 'deleted'. I'll use the example of (soft-)deleting ingredients from a recipe here.
I have many many places where I am requesting a recipe's ingredients. If possible, I would like to avoid updating all of these (and risk missing one). Instead, can I redefine the ingredients getter for the recipe class, where I will filter out the soft-deleted objects?
The few instances where I need the full list of ingredients (including soft-deleted) will use a new property, something like ingredientsIncludingDeleted.
Is this reasonable? Are there any notable side effects with using a custom getter that does something other than just returning what would be expected? Anything core data specific I should be aware of?
Yes you can do that. However, your approach is doing it backwards.
The default getter should not have any predicates. It should do exactly what it implies, which is an un-filtered result set. Think of someone reading your code 6 months later. Will they know that "ingredients" actually means "filteredIngredients" ? No.
So instead of changing the meaning of the default getter, create a new getter (named something like "filteredIngredients", and use that everywhere. This is the correct long term solution.
If you're worried about forgetting to change the property in some places, do this: temporarily rename your ingredients getter to something else, like "tempIngredients". This will cause compiler errors everywhere it is used. Fix all those compiler errors by using the appropriate getter, and then rename your default getter back to "ingredients".
Hope this helps.
I have a model named "Category". It is just a list of descriptions stored in the database. Now I want the category descriptions to appear in a drop down list.
Would the correct thing be to make an instance variable in the action where I say something like #categories = Category.all or do you use Category.all directly in the view?
What would be the shortcomings/advise against using the model directly in the view?
If Category.all is being called in the view only once, it's OK to write it directly. Else, it's better to write a helper rather than creating instance variables as per Rails convention. Something like
def all_categories
#all_categories ||= Category.all
end
It does a single query, if being used multiple times in the same view as well.
A common practice is to create the instance variable in the controller for the data which is specific to that request.
For you Category example, doing the query directly from the view is appropriate.
Could be in the view for that action, a partial, or even in the layout if it's used throughout the site.
A simple rule of thumb is to only save method results in instance variables if it saves you some code duplication because you want to use the result twice. Another rule of thumb is to never call methods that have side effects in your views. In this case it seems you are using a method without side effects once, so I would be okay with putting it in a view.
That said it is a lot easier to lose sight of your model method calls in your views than in your controller since they are mixed with the markup elements. That might cause you to overlook that you have called Category.all before when adding some new code in your view that also calls Category.all. Instead of easily noticing in your controller that you have an instance variable for Category.all, you are stuck with either going through your whole view or forgetting to do so.
Another case to make for using instance variables is that it can make your views easier to reuse. Instead of tightly coupling your view to the Category class, you might want to make the calls that are done on the instance variable generic enough to also be applicable to other models that can be viewed in a similar way. Using instance variables this way can help you take advantage of the duck typing that Ruby offers, but you have to weigh the ease of making things generic against the effort of remembering or looking up what kind of methods are actually available on the different kinds of objects that could be inside your instance variable.
Imagine a web application written in Ruby on Rails. Part of the state of that application is represented in a piece of data which doesn't fit the description of a model. This state descriptor needs to be persisted in the same database as the models.
Where it differs from a model is that there needs to be only one instance of its class and it doesn't have relationships with other classes.
Has anyone come across anything like this?
From your description I think the rails-settings plugin should do what you need.
From the Readme:
"Settings is a plugin that makes managing a table of global key, value pairs easy. Think of it like a global Hash stored in you database, that uses simple ActiveRecord like methods for manipulation. Keep track of any global setting that you dont want to hard code into your rails app. You can store any kind of object. Strings, numbers, arrays, or any object."
http://github.com/Squeegy/rails-settings/tree/master
If it's data, and it's in the database, it's part of the model.
This isn't really a RoR problem; it's a general OO design problem.
If it were me, I'd probably find a way to conceptualize the data as a model and then just make it a singleton with a factory method and a private constructor.
Alternatively, you could think of this as a form of logging. In that case, you'd just have a Logger class (also a singleton) that reads/writes the database directly and is invoked at the beginning and end of each request.
In Rails, if data is in the database it's in a model. In this case the model may be called "Configuration", but it is still mapped to an ActiveRecord class in your Rails system.
If this data is truly static, you may not need the database at all.
You could use (as an example) a variable in your application controller:
class ApplicationController < ActionController::Base
helper :all
#data = "YOUR DATA HERE"
end
There are a number of approaches that can be used to instantiate data for use in a Rails application.
I'm not sure I understand why you say it can't fit in a Rails model.
If it's just a complex data structure, just save a bunch of Ruby code in a text field in the database :-)
If for example you have a complex nested hash you want to save, assign the following to your 'data' text field:
ComplexThing.data = complex_hash.inspect
When you want to read it back, simply
complex_hash = eval ComplexThing.data
Let me point out 2 more things about this solution:
If your data structure is not standard Ruby classes, a simple inspect may not do it. If you see #<MyClass:0x4066e3c> anywhere, something's not being serialized properly.
This is a naive implementation. You may want to check out real marshalling solutions if you risk having unicode data or if you really are saving a lot of custom-made classes.