Rails Filtering Best-Practices - ruby-on-rails

I have a web app that has the following models: Shifts, Years, Checkouts, Sales and Visitors.
A Shift has_many Checkouts, Sales, Visitors
A Year has_many Shifts
Checkouts, Sales and Visitors belong_to Shift
Shifts belong_to Year
In my app, I want to have many different years present, although only ONE is active at any given time. When I display Shifts, for example, I only want Shifts from the active year to be displayed. That's easy enough.
What I'm having a real hard time with is writing a query to get all of the checkouts from the active year. Is there a simple way to go about doing this? The active year is actually set in a session variable called session[:current_year_id]. I thought the best way to go about doing it would be to do something like:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).checkouts
But that's not working for me. What do I need to change to get that to work?
Note that checkouts, sales and visitors DO NOT have their own year_id. They have a year_id through the shift that they belong to.
Resolved:
I upvoted all the answers, since all helped. I used the for_year lambda expression. I've upvoted all the answers.
The final answer was to use:
Shift.for_year(session[:current_year_id]).collect{ |shift| shift.checkouts }

You can create a scope in Shift model that takes current year as parameter:
class Shift < ActiveRecord::Base
has_many :checkouts
scope :for_year, lambda { |current_year_id| where('year_id = ?', current_year_id) }
end
and in the controller, you can get the checkouts for current year by chaining to the scope:
Shift.for_year(session[:current_year_id]).map(&:checkouts)

Using .where returns a relation, so there are two possible answers based on your intention.
If you want to find the checkouts belonging to the first shift, do:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).first.checkouts
If you want all checkouts for all shifts, do:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).collect{|shift| shift.checkouts}

You could try:
Shift.find_all_by_year_id(session[:current_year_id])
This assumes current_year_id is in the session.
I have had problems with using .where, which I fixed by using a dynamic finder.

Related

database design for composite elements

I'm building a site that tracks donations and sales of items in a school auction.
Items can be sold individually or in lots, which are just groups of items bundled for sale as a single unit (like a gift certificate for a dinner Item bundled with a gift certificate for movie tickets Item).
Both of these things (Items and Lots) share fields like name, description, value. But Items have additional fields, like the donor, restrictions of use, type of item, etc.
I started by creating a table called Lot and an association table that lets Lots contain 1+ Items.
That works great for Lots. But that leaves me with a problem:
When Buyers win I need to record the win and the price. I'm doing that with a Win table that associates the Buyer with the Lot and the winning price.
But how do I deal with all the Items that aren't assigned to Lots? Should every item be in a Lot, just singly? That would make sense because it would work with the Win table scheme above, but I would need to automatically create a Lot for every Item that isn't already in another Lot. Which seems weird.
I'm sure this is a simple problem, but I can't figure it out!
Thanks!
Your approach of treating every item as a lot should be the winning one. It may sound weird, but it will make things way easier in the long run.
I have to deal on a daily base with a database where a similar problem was 'solved' the other way round, meaning keeping bundles of items and items apart and that proved to be a great pita (and for sure I'm not talking about a flat round bread here).
This database is both backbone for statistical evaluations and a bunch of (web) applications and on countless occasions I run into trouble when deciding which table to chose or how to level the differences between those two groups in querying and in coding.
So, even if your project will be rather small eventually, that is a good idea.
Yes, you need to provide a method to put every item in a lot, but this trouble is to be taken just once. On the other hand your queries wouldn't become significantly more complex because of that 'extra' table, so I'd definitely would chose this way.
It sounds like you have an Auction model that could have one or many Items. Then you could have two different types of Auctions, Auction::Single and Auction::Lot. Price would be a column on Auction. Auction has many Bids which is a join model between the Auction and the User (or Bidder). That join model would also store the bid price. When the Auction is over, you could create a separate Win record if you want, or just find the Winner through the highest Bid from Auction.
It would be helpful if you showed some code. But, what you want is a polymorphic association. So, something like:
class Item
has_one :win, as: :winnable
belongs_to :lot
end
class Lot
has_one :win, as: :winnable
has_many :items
end
class Win
belongs_to :buyer
belongs_to :winnable, polymorphic: true
end

Rails: Track Points On A Weekly Basis

In my current application, I need the ability to track points on a weekly basis so that the point totals for the user reset back to zero each week. I was planning on using the gem merit: https://github.com/tute/merit to track points.
In my users profile I have a field that is storing the points. What I have been unable to locate is how I can have rails on an auto basis for all users clear this field.
I have come across some information Rails reset single column I think this may be the answer in terms of resetting it every Sunday at a set time -- but I am uncertain on this last part and in addition where the code would go (model or controller)
Also, would welcome any suggestions if their is a better method.
You'd be better making a Point model, which belongs_to :user
This will allow you to add any points you want, and can then query the table based on the created_at column to get a .count of the points for the timespan you want
I can give you more info if you think it appropriate
Models
One principle we live by is to extend our models as much as possible
You want each model to hold only its data, thus ensuring more efficient db calls. I'm not super experienced with databases, but it's my opinion that having a lot of smaller models is more efficient than one huge model
So in your question, you wanted to assign some points to a user. The "right" way to do this is to store all the points perpetually; which can only be done with its own model
Points
#app/models/point.rb
Class Point < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :points
end
Points table could look like this:
points
id | user_id | value | created_at | updated_at
Saving
To save the points, you will literally just have to add extra records to the points table. The simplest way to achieve this will be to merge the params, like this:
#app/controllers/points_controller.rb
class PointsController < ApplicationController
def new
#points = Point.new
end
def create
#points = Point.new(points_params)
#points.save
end
private
def points_params
params.require(:points).permit(:value).merge(:user_id => current_user.id)
end
end
You can define the "number" of points by setting in the value column (which I'd set as default 1). This will be how StackOverflow gives different numbers of points; by setting the value column differently ;)
Counting
To get weekly countings, you'll have to create some sort of function which will allow you to split the points by week. Like this:
#app/models/point.rb -> THIS NEEDS MORE WORK
def self.weekly
where(:created_at => Time.now.next_week..Time.now.next_week.end_of_week)
end
That function won't work as it is
I'll sort out the function properly for you if you let me know a little more about how you'd like to record / display the weekly stats. Is it going to be operated via a cron job or something?
Based on your description, you might want to simply track the users points and the time that they got them. Then you can query for any 1 week period (or different periods if you decide you want all-time, annual, etc) and you won't lose historical data.

how to avoid empty fields in a database

Frustrated with the Active Record Reputation gem, which I found very buggy, I'm trying to make my own reputation system for a Rails app. It's very primitive. I created a Contribution resource with a user_id and a value field, with an association between User.rb and Contribution.rb. Every time a user contributes to the app in some way, they get some points. If they ask a question, these lines get included in the create action of the Questions controller.
#contribution = current_user.contributions.build({:value => 3})
#contribution.save
If a user edits some Tags on the site, I do the same thing to reward superusers for their administrative work
#contribution = current_user.contributions.build({:value => 2})
#contribution.save
It then becomes very easy to calculate a user's total reputation.
One problem with this is that, in an imaginary world where users care about this app and their reputation, it would be very easy to game the system. For example, a user could just keep updating the categories or tags, and every time they do so they get 2 more points. Therefore, I wanted to somehow record what type of action the user did.
Right now, all of the work users can earn points for is somehow associated with a Question.rb, however, they get points for updating Tags, updating Categories, upvoting other people's answers etc, therefore merely storing the question_id in the contributions model wouldn't be sufficient.
Based on what I told you, can you give me some idea how I might build out the Contributions resource in order to accomplish what I want?
For example, I thought of one way of doing it that would have left a lot of null fields in my database, so I assumed it wasn't a good way. I could add a question_id and several boolean fields such as 'answering_question' 'updating_category' 'updating_tags' and each time an action is performed, record with a 'true' whether, for example, 'updating_category' is being performed. However, as mentioned, if I start rewarding lots of different types of contributions, there's going to be a lot of columns in each row that aren't being used.
I'm not sure if that's a 'real problem' (i've read about it but not sure how necessary it is to avoid), or if there's a better way of recording what type of activity each user is engaging in to earn points.
some of the current associations
User has_many :answers
Question.rb has_many :categories
Question.rb has_many :tags
for my rails application I am using thumps_up gem which is better than active_record_reputations_system ,Its more simple.
https://github.com/bouchard/thumbs_up

Appointment scheduling in Rails?

I have a simple application with a customer and appointment models, system admin can create customer and create appointments for that particular customer, is there a Rails plugin that handles scheduling to ensure no two appointments overlap each other? i.e no two appointments at the same time.
A plus would be if I can set up more than one schedule, i.e shop has 2 instructors for lessons, when selecting appointment we can select which instructor etc.
What's the best way to do this?
Thanks
def is conflicting
if(appointemnt1_start < appointment2_end && appointment2.start < appointment1.end)
return true
end
end
Not that I know of. It's fairly simple in theory, though. Subtract Time B from Time A, and if they're within X minutes of each other (however long an appointment takes), there's a conflict.

How to best model and search seasonal availability with Rails

So I have an app where I am tracking a number of things, including flowers. Depending on the type of flower they can be available from places during certain spans of time during the year. Sometimes they can even be available during multiple spans of time (eg domestically from Mar-Jun, but can be found internationally from Sept-Dec).
What I am looking to be able to do is search for a specific date and determine all the different flowers that would be available on that date.
My idea was to have an Availability model which had a belongs_to relationship with a Flower. It would have a start_date, an end_date, and a flower_id. The problem was that dates in rails tend to be specific points in time, eg 2009-10-13. If I said a flower was available from 2009-10-01 - 2009-12-31 when 2010 came around I wouldn't see it as available.
So then I thought maybe I could have some sort of cron job that went through daily and changed the years on availability records as their end dates came up.
Maybe this is the right approach, but it feels a bit clunky. I looked through a few gems/plugins and couldn't find anything in particular that would fit my need.
Anyone have any insight?
Thanks in advance...
Given the cyclical nature of months it can be difficult perform a query to quickly select months where a given set of flowers is available.
I like the availability model, but think you should be saving just the month number (eg: October is saved as 10 in the start_month/end_month fields). Use Date::MONTHNAMES to make things human readable. (eg: Date::MONTHNAMES[10] => "October")
This allows you to easily form a named scope in Availabilities to choose what's available now.
class Flower < ActiveRecord::Base
has_many :availabilities
named_scope :available_today, lambda
month = Date.today.month
{:include => :availabilities, :conditions =>
["(availabilities.start_month < availabilities.end_month AND \
availabilities.start_month <= ? AND availabilities.end_month >= ?) \
OR (availabilities.start_month > availabilities.end_month AND \
availabilities.start_month >= ? AND availabilities.end_month <= ?)",
month, month,month,month]}
named_scope :red, :conditions => {:colour => "red"}
end
Now you can do Flower.available_today.red to get a list of red flowers available now.
Unfortunately the named scope might populate the association with the availabilities (Flower.available_today.first.availabilities) that you don't want. I can't be sure without testing it, and I don't have the environment to do so right now. Worst case scenario you can use #flower.availabilities.select with similar arguments to the queries to prune the list.
I like the Availability model idea, but I'd store just a start_month and end_month (both integers) and a notes field (for stuff like "Internationally" or "Domestically"). Then when you have a date, you just get the month field and compare to the set of ranges you have for each flower.
That might be a little compute-intensive if you have a lot of flowers. You could instead store a single Availability row for each flower, with 12 integers - one for each month - and 12 notes fields. Or if you won't have a lot of notes, you have an AvailabilityNotes model. Availability then has_many :availability_notes.
If it's for seasonal items, you could just have a (start/end)month or season field.
First of all, is there a customer demand to get this "right?" Who wants this feature, and how do you know they want it? Have you modeled the user's workflow to determine how this actually fits into ordering behavior? Do you really take orders for flowers a year or more in advance? Is availability only based on the season -- there's no dependency on suppliers or other events that can change year-to-year? And are you anticipating that you'll always have the same inventory next year that you will this year? Or is pinning an availability date on a flower not a guarantee?
If this is just for general information purposes -- you'd like people to know what sorts of flowers they can order at what times of year -- then I wouldn't give the user a "pick a date" function at all. I'd just give them a dropdown: either for four values for seasons, or twelve for months. If they just want to know "Is it likely I can get lilies for my mom's birthday?" that's fully sufficient.
And you could model it very simply, with four or twelve boolean values in your model, and four or twelve checkboxes on your flower create/edit form. (Yeah, I know, it's "purer" to do a :has_many association, but unnecessary; the number of months in the year isn't going to change.)

Resources