I'm writing a super simple web application that presents different counters for different events. My database table is filled with records representing the different events. the table has 2 colums name and date
Now before I can present the counters for every event, I need to
calculate the time left to its date
and make a string that either says something like 5d, 4h, 23m or completed
Where would you guys put this logic? Please bear with me, I'm a beginner.
ps. I'm trying to make a proper MVC application with Ruby on Rails
Sometimes the answer to "does this go in the model, the view, or the controller?" is "no".
Like others have said - you want the controller to be as thin as possible. It's job is to relay 'events' and do basic set up of other objects. According to conventional wisdom, the model should encapsulate your business logic. In Rails, a model usually does both persistence and business logic. In other words, if you have rules for how data is to be manipulated or how it is to be aggregated, that goes in the model. On the other hand, people have started arguing that managing both persistence and business logic is too much responsibility for one object.
The responsibilities you describe are about presenting values. To do this you could use the Presenter or Decorator pattern. In essence, you create a class of object whose responsibility is to take data from another object and prepare it for presentation. The Draper gem provides some utilities for doing just this.
In your event.rb, create a method called time_left (taken from https://stackoverflow.com/a/19596579/1489088):
def self.time_left
# Calculate time left for event start
current_time = Time.now
# date is retrieving the date for your model, but it has to be a datetime field and not a date field.
seconds_diff = (date - current_time).to_i.abs
hours = seconds_diff / 3600
seconds_diff -= hours * 3600
minutes = seconds_diff / 60
seconds_diff -= minutes * 60
seconds = seconds_diff
"#{hours.to_s.rjust(2, '0')}:#{minutes.to_s.rjust(2, '0')}:# {seconds.to_s.rjust(2, '0')}"
end
Why in your model, simply because this calculation is specific to your events, and you want to be "playing" with your model data here and most of your data behavior to be handled here. I can't really dig in into this, others can do it better and there is a lot of ressources on the subject if you search for it (Search for example "Fat Model, Skinny Controller" in Google).
Now assuming you want the time left to be calculated every time you load the event page, you must specify it in your controller :
events_controller.rb :
def show
set_event
#time_left = #event.time_left
end
Then basically call #time_left in your view.
I'm not sure what #CaffeineCoder meant by "Put as much minimum logic in model , models are meant for only relationships", but along similar lines i would say:
Keep as much of your logic in your models as possible.
Models should hold all your business logic, controllers should do as little as possible, and views should handle anything which is to do with formatting data and presenting it to the user. Repeated bits of formatting can go in helpers, which is like a subsection of the View.
You don't tell us much about your schema so i can't advise on specifics, but you could for example have a method in Event like "time_remaining", which returns a number of seconds, and then have helpers which take this value and return "5d" or "4h" or whatever.
Related
I'm building an application that is based on a calendar and (with basic functionality expected from a calendar).
As the calendar will have such a fundamental part of the application I don't want to rely on any gem but build the calendar myself.
I don't know which route to go: to have a day-model representing each day with a unique record (that events can reference by day_id) OR render the calendar "on the fly" based on a Date class (and events would then reference the date).
In short: What model design would be the most efficient to render a calendar?
You don't need to have a model at all. The following code below will do the trick.
Grab the code from https://gist.github.com/RichIsOnRails/5535564 and drop it in a helper, use like so (haml below, so adapt it to meet your own needs.) Note that i'm rendering events in this calendar, but you don't have to.
= calendar #date do |date|
.text-left=date.day
- if #events_by_date[date]
- #events_by_date[date].each do |event|
.event=event.title
I will do an article on my website in the near future that goes over this in detail. Good luck!
Unless there is something specific about the days you would like to model, you may be able to just have an events model, with each event having a date field. This approach is simpler and I think preferable. Since there are a practically infinite number of dates, one big advantage of this approach is that you won't need to manage all the logical inconsistencies of keeping a finite subset of dates in your database.
Even if you find a need for a day model, you can do this without tying the model to the database, and thus avoid having a record in your database for every possible date. This might be useful if you want a method that tells you if a day is a work day, or if you want to find a collection of days. For more on how to make a model that doesn't require a database table behind it, see this stackoverflow question: Tableless model in rails 3.1
I built a small site where I gather information of some cultural events happening in the area. I want to extend the Event model so it can handle 3 different 'case scenario' of events.
'i.e. Single'. Normal event with start date and finish date.
'i.e. Band on tour'. Same event but happening at different times.
'i.e. Museum'. Monday to Sunday timetable.
I thought that the best approach for the 1 and 2 types will be:
Event model
# holds title, description, price....
has_many :dates
accepts_nested_attributes_for :dates
Date model
# holds start_time(:datetime) and finish_time(:datetime)
belongs_to :events
I am not sure how to approach the 3rd type. Or what is more important, how to build it in a way that makes future interactions easier to code... like building a SEARCH engine.
Idea:
Building another model called Timetable?
This could hold:
boolean types -> Mon - Tue... -Sun
time type -> Mon_start - Tue_start... - Sun_start - Mon_finish - Tue_finsih... - Sun_finish.
However, this seems pretty complicated to update the search engine(which has a date field/parameter) to iterate through all these 3 types.
Any ideas/experience which may clarify the path to take? Many thanks in advance!
One way that would work well would be to make Date abstract, and then make three more models that extend Date.
I am trying to design the points system for a game application, and having some issues figuring out the best way to setup the models. The basic needs are:
Each user can be assigned essentially a "PointsCalculator" object, responsible for keeping track of the points that the user is earning
Each user's "PointsCalculator" logic could potentially be different. For example, one user might earn an extra 100 points every time he plays for more than an hour consecutively. Another user might earn double points between the hours of 8PM and 10PM.
We are trying to figure out the best way to create a model setup that will allow us to store logic that can be customized for each user, but is still DRY and testable.
I don't need anyone to write code for me - I would more just appreciate the feedback of more experienced developers, or anyone who has tackled a similar issue in the past.
Note: Above has been edited for additional clarity, but let me know if it is still unclear.
Difficult question which depends on your complexity level:
If all Points Calculator are well defined, you should head towards Single Table Inheritance (STI). The most simple case.
But if much flexibility is demanded, this becomes harder and you should consider the inclusion of methods dynamically, a la DCI. It's the most complex. See details below.
Ex: you store a list of calculator behaviors in a serialized object within your model:
behaviors = [ 'behavior1', 'behavior2' ]
Each behavior is linked to a class or module with the same name, eg: Behavior1 and Behavior2.
Then you add behaviors to your object on the fly whenever you need them, for instance in an after_initialize.
after_initialize :set_behaviors
def set_behaviors
behaviors.each do |b|
self.extend(b.constantize)
end
end
I have models for Products and Statement_Sales and want to perform various Sum and Group actions for presenting sales reports and basic analytics in my views. Where is the best place to put these, Model or Controller, and how do I then access them in my views?
At the moment I can easily bring in line by line sales date using something like this in my view:
<% #product.statement_sales.each do |sales| %>
<td><%= sales.units %></td>
<% end %>
But this is line by line data, I want to be able initially give a total by date, then totals for 7 days, 1 Month, 3 Months, 1 Year etc etc.
In console i've figured out grouping by date using:
p.statement_sales.group(:date).sum(:units)
Not sure how to bring that into the app and take it beyond grouping by one date.
Fairly new to Rails (after years of PHP) so hitting a few walls with the differerent approach. I'm sure this is actually quite easy!! Thanks in advance.
The rule of thumb is "fat model, skinny controller". This means you should push most of your logic into the model and keep the controller actions as simple as possible.
For you particular scenario, you will want to look into Rails scopes. Scopes allow you to encapsulate and reuse a complicated query such as the ones you describe above. For example, assuming you have a StatementSale model that belongs_to a Product (I don't know the specifics of your object graph):
class StatementSale < ActiveRecord::Base
scope :units_by_date, group(:date).sum(:units)
end
This would allow you to call #product.statement_sales.units_by_date in your view.
You can also build a scope that takes a parameter. For example, say you wanted to be able to pass in a parameter to group by dynamically, your scope would look like this:
scope :units_grouped_by, lambda { |p| group(p).sum(:units) }
Without knowing the details of the more complicated queries that you'd want to perform, scopes are definitely the place to house all of that logic.
I have an ASP.NET MVC site and I am trying to figure out separation of controller and model (repository) and HTML helper functionality.
The goal is to query a database table of photo albums information and display it grouped by year.
The steps are:
Query database and return datatable of the database information.
Convert Datatable to AlbumCollection (List)
Bucket albums by year into ALbumDictionary
Render each year in a seperate HTML table.
Given this request, I could see:
1,2,3 all in the model and the controller simply binds the View to the AlbumDictionary model
or
1,2 in the model and bind to the AlbumCollection and 3 in a HTML ViewHelper
or
1,2 in the model 3 in the controller and bind to the Albumdictionary
Thoughts?
Doing every conversion in the first loop would have the best performance but I am not sure it is the best separation of concerns.
In particular to the above question, generic feedback would be interesting: when does separation of concerns overrule performance or vise versa?
Having been a user of some truly horrendous software that I'm sure looked good from an object-oriented perspective to the designers and was even possibly easy to maintain, I want to point out that the users will come down on the side of performance almost every time.
If the performance difference is negligible, follow the separation of concerns, if it is not, do what it takes to get the best performance. We need to stop worrying as much about the extra few minutes to possibly maintain (maybe touching that code once a year once it's in prod) and more about slowness for all the users every day issue. We like to say that development time is so expensive that we need to minimize it, but the truth is that development time is often far cheaper than the amount of time we are asking our users to waste daily.
I would try to keep the Model clear of anything that has to do with rendering.
I see the grouping by year pretty close to rendering. Thats why I would not put it into Model and also not into the Controller. A common aproach is to have a Model of Poco and DAL/BLL and anonther Model called ViewModel (the Model used by the strongly typed View). This is a good place to prepare the objects for rendering.
In ViewModel I would use Linq to group the albums by years. This will hopefully be fast enough.
I would do the bucketing in the controller only if either:
the bucketing occurs just once and I can do that with one or two simple statements;
It occurs more than once but I can do that with just a AlbumDictionary.BucketByYear() statement.
Otherwise I'd use models to do that.