I have two tables: Persons and Statuses and have created two classes Person and Status. I am using following code to show the error message "Only one status is allowed". The code is not working - I think there is some problem in my If statement.
<ul>
<% Person.all.each do |person| %>
<li>
<%= person.name %>
<% if status.size >= 1 %>
<em>Only one status is allowed</em>
<% end %>
</li>
<% end %>
</ul>
Table Persons
U_Id Name Place
1 James Florida
2 Mark California
3 Steve Newyork
Table Statuses
Id Status U_Id
1 Hi 1
2 OMG 2
3 Bye 3
4 Help me 2
Problem: Mark has posted 2 status his U_Id is 2, I want to show him a error message like Only one post is allowed. How this can be done?
Update:
Person class
class Person < ActiveRecord::Base
validates_presence_of :name
end
Your programming logic is incorrect. You are trying to impose a limit on the number of status messages a user can have, but it seems you are enforcing that limit too late, because you are printing an error message when the status is displayed rather than when it is submitted. The people viewing these messages are presumably other users and they hardly care if Mark violates your design constraints.
You have two options.
Limit the number of statuses one a user can have to one.
class User < ActiveRecord::Base
has_one :status
end
This will allow you to do:
steve = User.find(3)
steve.status
=> "Bye"
Only display the last one
Alternatively, you can allow unlimited statuses, but only display the latest one.
class User < ActiveRecord::Base
has_many :statuses
end
mark = User.find(2)
mark.statuses.last
=> "Help me"
On a side note... if users truly only have one status and the former statuses do not matter, then you should consider removing the status model and including the status as a string attribute on the user model. This will, in most cases, improve database performance.
I agree with Stas. status.size will not know what "status" is referring to. It seems like you are trying to refer to the status of each individual person, so you would need something like "person.status.size >= 1".
However, looking at your Person Class, it looks like you might not have the relationship set up yet. You need to include code in your Person class specifying that a Person has_many :statuses. So first do this and make sure that User.first.statuses works, then add the similar code to your views.
in user.rb I admit you have has_many : statuses
and in status.rb, belongs_to :user
<ul>
<% Person.all.each do |person| %>
<li>
<%= person.name %>
<% if person.statuses.size >= 1 %>
<em>Only one status is allowed</em>
<% end %>
</li>
<% end %>
</ul>
but if you want a person to have only one status, why don't you check this when a status is created? or better, when he is trying to access the new action for status check if he has a status and redirect him back with a message saying "You have a status,you can have only one."
After this you can easy use:
user has_one :status
statuses_controller check:
def new
if current_user.status.present?
redirect to :back, error: "Only one status for user"
else
#status = current_user.status.new
end
end
and when doing Person.all.each do |person| you can call directly person.status and it will be first and only one in db without needing to use an if statement. But this depends on how your app should work I guess.
Related
I am trying to build a simple donation app in rails. In this application, patrons would give amounts of money to clients. Both the patron and the client share large amounts of the same functionality. They are both linked to a user and have a username. However, the client is also supposed to have a content_type and a content_list property. At first glance, my guess is that I want to have both my patron and client inherit from the account class. However, the client has additional functionality, which seems to preclude any STI-based implementation (though I will be the first to admit that my understanding of STI is shaky at best). As it stands, it seems to simply make more sense to write out two separate resources, but I would like to keep my code as DRY as humanly possible. Is there a simple way for me to create the behaviors I want through inheritance, or should I simply go with overlapping resources?
Here's an idea, as both of your user-types share the same basic functionalities.
As you said, make one make one unified User or Account model. Include the database fields customer (as boolean) and patron (also as boolean).
In the signup process, the user can then select if they're a patron or customer, as they would regardless.
Then inside your view, you can then call, if you use Devise for instance (which I personally think is great)
<% if current_user.patron == true %>
<!-- all relevant UI functionality for patrons -->
<% end %>
or if the User is a customer
<% if current_user.customer == true %>
<!-- all relevant UI functionality for customers -->
<% end %>
Or if you want to loop through a list of patrons or customers:
<% #user.where(:customer == true).each do |users| %>
<% #user.first_name %> <% #user.last_name %>
<% end %>
These are just basic examples, but it would do just fine for what you're trying to achieve.
P.S You could also create one migration called "account_type" as a
string and then with the help of radio_buttons in the signup process
store the account_type as a string value.
<%= f.radio_button :account_type, "customer" %>
<%= f.radio_button :account_type, "patron" %>
I actually think that would be better. Then you would split the views up
like this:
<% if current_user.account_type => "customer" %>
Show the list that only customer should have or see.
<% end %>
<% if current_user.account_type => "patron" %>
Show the list that only patron should have or see.
<% end %>
<% #user.where(:account_type => "customer").each do |users| %>
<% #user.first_name %> <% #user.last_name %>
<% end %>
Regarding my question: Will a patron ever be a client? Will a client ever be a patron?
If the answer is "yes", you should consider creating a separate entity/model for the resource they're relating to.
With model DonationCampaign (attributes client_id, among others):
has_one :client, :class_name => 'User'
has_many :patrons, :class_name => 'DonationCampaignPatron'
DonationCampaignPatron (attributes patron_id, among others):
belongs_to :patron, :class_name => 'User'
This allows you to keep the shared functionality of User and then extend functionality to specific campaigns, without having to make other models messy, and keeps things DRY.
If a DonationCampaign could then have multiple users (as administrators, per se), to extend, a DonationCampaignRole model would be required, donation_campaign_id, user_id, role
Say you're using CanCanCan,
if can? :manage, #campaign
if can? :contribute, #campaign
:contribute would have to be added to ability.rb and could simply be #campaign.client != User
Also, STI example (the good kind, at least):
class Animal < ActiveRecord::Base
def says
raise "Implement on subclass"
end
end
class Cat < Animal
# always has a type of 'Cat'
def says
"meow"
end
end
class Dog < Animal
# always has a type of 'Dog'
def says
"woof"
end
end
And there's only one table: animals
Edit: Based on your response: From what I can learn by briefly using Patreon they have a single User model (for authentication), and then they likely have a creator_page_id in the User column. They then have a separate model CreatorPage which has all the "Client" (in your terms) info associated with it.
Personally, I would stick with a single User model for authentication and then implement the aforementioned DonationCampaign/DonationCampaignPatreon business logic. It's the most extensible with the least amount of effort (both long and shot-term).
If, for whatever reason, a Client is restricted to contributing to other Clients once they are a Client, I would forego using STI on the User model.
Using Rails 4, in a controller I would like to add an attribute to an instance variable.
Sorry for the poor example, I'm trying to keep it simple.
E.g. In a controller, I create a new instance variable by looking up some users named John. Now, in my controller, I would like to sum up all the ages for all Users named John, put that summed age back in to the instance variable so it is available to the view.
The User model has attributes 'id', 'name' and 'age'.
#foo_users = Users.where(name: 'John')
#foo_users.each do |foo|
#foo_users.age_sum = Users.where(name: 'John').sum(:age) <-- this does not work
end
I have no need to save that summed age back to a database, since I will only use it in one view. I would like to be able to display all the users:
<% #foo_users.each do |user| %>
User name: <%= user.name =>
Sum of ages: <%= user.age_sum %>
<% end %>
Update: I might have over simplified my example. Here is a closer to reality example.
A company owns hotels. Hotels have Rooms. Management software delivers to the company daily Hotel_Statistics via an API. For lack of a better word, these Hotel_Statistics contain the hotel_id, daily check-ins, daily check-outs. In the company's back-office Rails app that I am working on, on the page displayed there is a table of hotels with their given most recent statistics. One line would look like:
Hotel Id: 123
Daily check-ins: 50
Daily check-outs: 48
Hotel Id: 124
Daily check-ins: 35
Daily check-outs: 37
The company wants to also display the running sum of the last 30 days of check-ins (outs, net check-ins).
To accomplish this, in my controller, I find the Hotel_Statics for the most recent date (normally yesterday).
latest_stat = HotelStatistic.order('date DESC, hotel_id DESC').first
#latest_date = latest_stat.date
#recent_stats = HotelStatistic.where(date: #latest_date).order('hotel.id ASC').all
I display the details of #recent_stats in my view.
Now, I would like to display in my view the sum of the last 30 days of #recent_stats.check_ins for each Hotel. My idea was to sum up the the last 30 days of check_ins statistics for a given Hotel like:
#recent_stats.each do |stat|
#last_30_days_check_ins = HotelStatistic.where(hotel_id: stat.hotel_id).where("date >= ?", Date.today - 30).sum(:check_ins)
end
The math works, but I need a way to access the 30 day sum variable for each hotel. I was a hoping to make this easy in the view by adding the hotel 30 day sum to the #recent_stats instance variable so in my view I could do:
<% #recent_stats.each do |statistic| %>
Hotel Id: <%= statistic.hotel_id %>
Daily check-ins: <%= statistic.check_ins %>
Last 30 days check-ins: <%= statistic.last_30_days_check_ins %>
<% end %>
Does this more realistic example change anything in your suggested answers? Thanks
The type of #foo_users is ActiveRecord::Relation. Trying to add age_sum as a new attribute to an ActiveRecord::Relation object doesn't make sense because semantically age_sum is not an attribute of ActiveRecord::Relation objects. It's better to store the sum of ages in a new instance variable, for example #user_age_sum.
UPDATE
Try the following
class HotelStatistic < ActiveRecord::Base
belongs_to :hotel
end
class Hotel < ActiveRecord::Base
has_many :hotel_statistics
def last_30_days_check_ins
self.hotel_statistics.where("date >= ?", 30.days.ago).sum(:check_ins)
end
end
Keep the existing code for building #recent_stats in the controller
In the view
<% #recent_stats.each do |statistic| %>
Hotel Id: <%= statistic.hotel_id %>
Daily check-ins: <%= statistic.check_ins %>
Last 30 days check-ins: <%= statistic.hotel.last_30_days_check_ins %>
<% end %>
Using select should solve your problem:
#users = User.select("*, SUM(age) as age_sum").where(name: 'John')
Now each User in the #users array will have a age_sum property. This is not 100% ideal as the value of the property will be the same on each instance, but it will work with how you've setup your view.
Edit
It's possible to dynamically define a method on an instance manually:
#foo_users.each do |foo|
def foo.age_sum; Users.where(name: 'John').sum(:age); end;
end
However while this is possible, it would have to be a very good use case to justify the negative impact this may have (such as on how readable, efficient and maintainable the code is). There are probably much better OO ways to solve the same problem
I'm attempting to make an invoice application. Here are my models which are related to my question:
UPDATE: Model information has changed due to recent suggestions
Invoice
> id
> created_at
> sales_person_id
LineItem
> id
> invoice_id
> item_id
> qty_commit (inventory only)
> qty_sold
> price (because prices change)
> ...etc
Item
> barcode
> name
> price
> ...etc
Invoice has_many items, :through => :line_items. Ditto for Item. What I want to do is that when I create a new invoice, I'd like the form to be populated with all available Items. The only time I don't want all items to be populated is when I'm viewing the invoice (so only items which exist in the LineItems table should be retrieved). Currently - and obviously - a new Invoice has no items. How do I get them listed when there is nothing currently in the collection, and how do I populate the form? Also I'd like all products to be available when creation fails (along with what the user selected through the form).
UPDATE: I can create items through the controller via the following:
#invoice = Invoice.new
# Populate the invoice with all products so that they can be selected
Item.where("stock > ?", 0).each do |i|
#invoice.items.new(i.attributes)
end
This is of course my crude attempt at doing what I want. Visually it works out great, but as predicted my form id's and such are not playing well when I actually attempt to save the model.
LineItem(#37338684) expected, got Array(#2250012)
An example of the form:
# f is form_for
<% #invoice.items.group_by{|p| p.category}.each do |category, products| %>
<%= category.name %>
<%= f.fields_for :line_items do |line_item| %>
<% for p in products %>
<%= line_item.hidden_field :tax_included, :value => p.tax_included %>
<%= p.name %>
$<%= p.price %>
<% end %>
<% end %>
<% end %>
First of all, if you explicitly want to have a join model with additional attributes in it, you should use has_many :through instead of has_and_belongs_to_many. See the RoR Guide to the differences of the two.
Second, there is no single solution for what you want to reach. I see there two typical usages, depending on the mass of possible instances, one is better than the other:
Use radio buttons to select (and deselect) where a relation should be created or deleted. See the railscast #165 how to do part of that.
You could use select menus with a button to add a relation. See railscast #88. The added relation could be shown in a list, with a delete button nearby.
Use token fields (see railscast #258) to autocomplete multiple entries in one single text entry field.
In all the situations, you normally have to check at the end, if
a relation should be deleted
kept
or created
I hope some of the ideas may show you the right solution for your problem.
I have a one-to-many relationship in my rails application:
I have a User class that can have many Devices
I have a Device class that belongs to a User
My Models are designed like this:
class User < ActiveRecord::Base
has_many :devices
end
class Device < ActiveRecord::Base
belongs_to :user
end
Regarding views, when I want to display all Users and list their associated Devices I use this code:
<%= user.devices.each do |device| %>
<%= device.id %>
<% end %>
The output is: (only 1 device right now)
1 #<Device:0x101f45e50>
What I do not understand is why
#<Device:0x101f45e50>
is showing up after the id
replace equal sign
<% user.devices.each do |device| %>
<%= device.id %>
<% end %>
To give a litte more context so you know why this occurred, the = parses the output AND displays the result from the statement in the resulting HTML, where the - parses the line but does NOT display the result — since ruby passes a result at every new statement, you must put your = and - in the right spots.
Documentation is your friend (this is for HAML, but is still a good explanation)
I'm attempting to make an invoice application. Here are my models which are related to my question:
UPDATE: Model information has changed due to recent suggestions
Invoice
> id
> created_at
> sales_person_id
LineItem
> id
> invoice_id
> item_id
> qty_commit (inventory only)
> qty_sold
> price (because prices change)
> ...etc
Item
> barcode
> name
> price
> ...etc
Invoice has_many items, :through => :line_items. Ditto for Item. What I want to do is that when I create a new invoice, I'd like the form to be populated with all available Items. The only time I don't want all items to be populated is when I'm viewing the invoice (so only items which exist in the LineItems table should be retrieved). Currently - and obviously - a new Invoice has no items. How do I get them listed when there is nothing currently in the collection, and how do I populate the form? Also I'd like all products to be available when creation fails (along with what the user selected through the form).
UPDATE: I can create items through the controller via the following:
#invoice = Invoice.new
# Populate the invoice with all products so that they can be selected
Item.where("stock > ?", 0).each do |i|
#invoice.items.new(i.attributes)
end
This is of course my crude attempt at doing what I want. Visually it works out great, but as predicted my form id's and such are not playing well when I actually attempt to save the model.
LineItem(#37338684) expected, got Array(#2250012)
An example of the form:
# f is form_for
<% #invoice.items.group_by{|p| p.category}.each do |category, products| %>
<%= category.name %>
<%= f.fields_for :line_items do |line_item| %>
<% for p in products %>
<%= line_item.hidden_field :tax_included, :value => p.tax_included %>
<%= p.name %>
$<%= p.price %>
<% end %>
<% end %>
<% end %>
First of all, if you explicitly want to have a join model with additional attributes in it, you should use has_many :through instead of has_and_belongs_to_many. See the RoR Guide to the differences of the two.
Second, there is no single solution for what you want to reach. I see there two typical usages, depending on the mass of possible instances, one is better than the other:
Use radio buttons to select (and deselect) where a relation should be created or deleted. See the railscast #165 how to do part of that.
You could use select menus with a button to add a relation. See railscast #88. The added relation could be shown in a list, with a delete button nearby.
Use token fields (see railscast #258) to autocomplete multiple entries in one single text entry field.
In all the situations, you normally have to check at the end, if
a relation should be deleted
kept
or created
I hope some of the ideas may show you the right solution for your problem.