Case statement for STI type column - ruby-on-rails

I have a User model that can have many Websites. For example, I will be able to call User.websites and it will pull up an ActiveRecord array of all of the websites that the User owns. But each website uses Single Table Inheritance (STI) and a type column that will determine the type of the website, like commercial, advertising, or forum. My problem is, that I was to make a case/when statement that runs a function if the the User has a website that is of a certain type. for example:
#user = User.first
case #user.websites
when includes?(Commercial)
'This user has a commercial website'
when includes?(Forum)
'This user has a forum website'
when include?(Ad)
'This user has a advertisement website'
end
Does anyone have a clue on the best way to do such a thing?

Shortest way to do this is using a the map method. Just one line!
#user.websites.select(:type).uniq.map{
|x| puts "This user has a #{x.type.downcase} website"
}
Or if you want to have these strings in an array.
#user.websites.select(:type).uniq.map{|x| x.type.downcase}
If you want to run functions. I would advice naming the functions in a friendly way such as commercial_cost, forum_cost, etc.. so that you can use the send method which makes your code compact.
#user.websites.select(:type).uniq.map{ |x| #user.send(x.type.downcase + "_cost") }

def types
'This user has following websites: ' +
websites.pluck(:type).map(&:downcase).join(', ')
end

#user.rb
def return_data(*types)
types.each do |t|
if Website.where(:user_id => self.id, :type=> t).length>0
send "method_for_owners_of_#{t}_site"
end
end
end
def method_for_owners_of_Commercial_site
end
#...
#user.return_data(["Commercial"])
#user.return_data(["Commercial", "Ad"])
...
According to Rails API
When you do Firm.create(name: "37signals"), this record will be saved
in the companies table with type = “Firm”.

Related

How do I enter a record for every user where condition is true in Rails

So I have app that has political candidates.
When a new political candidate is entered, I want to enter a notification into the notifications table for every user that's state is equal to the state of the new candidate being entered.
Ultimately, I want to enter in records to the notification table for every single user where that condition is met.
I know I'm way off, but here's where I'm at now. I'm trying to loop through each user and then enter this record when that condition is true.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
User.each do |u|
if Candidate.state == User.state
#notification = Notification.new(:message => 'Message', :user_id => U.id)
#notification.save
else
end
end
else
render('new')
end
end
The candidate is created with this code, but the notifications aren't working. Basically I have two users where their state equals "Arizona" and I would expect if I create a new candidate where the state is "Arizona" that I should get two record into notifications, one with each user ID.
I think you got a bit mixed up between classes and instances. Here's the relevant bit:
#candidate = Candidate.new(candidate_params)
...
User.each do |u|
if Candidate.state == User.state
...
end
end
In your code Candidate is a class, and #candidate holds the recently created instance of a Candidate. Likewise, User is a class and u holds a User instance (on each loop iteration). Your comparison should actually use the instances rather than the classes:
if #candidate.state == u.state
Having sorted that, it's worth noting that your code has a couple of other errors -- User.each won't work. You need to specify a selector to get a list of User objects to loop through. One way would be to call User.all.each (which looking at your code is probably what you were trying). That pulls all User objects. But, since users can be from anywhere, if you do that you will cycle through a lot of users you don't need to.
Since all you need is users whose state matches the new candidate, you can use the where() method to pre-filter the list you are looping through. That way you don't need the if at all.
#candidate = Candidate.new(candidate_params)
...
User.where(state: #candidate.state).each do |u|
#notification = Notification.new(message: 'Message', user: u)
#notification.save
end
The other problem in your code is in the line to create a notification. You use U.id but the loop variable is lower case u. As an added tip, you don't need to set the object ID specifically. If you just pass the User object (as in the code above), Rails is smart enough to figure out the rest.
For performance don't iterate all users, you can search users that match the candidate's state then create notification for each user.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
users = User.where(state: #condidate.state)
users.each { |user| #notification = Notification.create(:message => 'Message', :user_id =user.id } if users
else
render 'new'
end
end

Get specific result by checking a simple variable inside a double array

I have a #customers variable in which each customer has multiple contracts. Each of those contract has multiple users.
What I would like to do is to get all the customers that have a specific user
I tried using the find method but I don't understand how to do that inside a what seems to be double array.
Currently I have this:
cust.each do |c|
if c.contracts.where(users.find(session[:login]))
#customers << c
end
end
What am I missing here?
I think you have to mention user field in where clause, Can you try below code
cust.each do |c|
if c.contracts.where(user: users.find(session[:login]))
#customers << c
end
end

How to store the result of my algorithm?

I have an algorithm that searches through all of my sites users, finding those which share a common property with the user using the algorithm (by going to a certain page). It can find multiple users, each can have multiple shared properties. The algorithm works fine, in terms of finding the matches, but I'm having trouble working out how to store the data so that later I'll be able to use each unit of information. I need to be able to access both the found users, and each of the respective shared properties, so I can't just build a string. This is an example of the output, being run from the perspective of user 1:
user 4
sharedproperty3
sharedproperty6
user 6
sharedproperty6
sharedproperty10
shareproperty11
What do I need to do to be able to store this data, and have access to any bit of it for further manipulation? I was thinking of a hash of a hash, but I can't really wrap my head around it. I'm pretty new to programming, and Ruby in particular. Thanks for reading!
EDIT - Here's the code. I'm fully expecting this to be the most incorrect way to do this, but it's my first try so be gentle :)
So if I'm understanding you guys correctly, instead of adding the interests to a string, I should be creating an array or a hash, adding each interest as I find it, then storing each of these in an array or hash? Thanks so much for the help.
def getMatchedUsers
matched_user_html = nil
combined_properties = nil
online_user_list = User.logged_in.all
shared_interest = false
online_user_list.each do |n| # for every online user
combined_properties = nil
if n.email != current_user.email # that is not the current user
current_user.properties.each do |o| # go through all of the current users properties
n.properties.each do |p| # go through the online users properties
if p.interestname.eql?(o.interestname) # if the online users property matches the current user
shared_interest = true
if combined_properties == nil
combined_properties = o.interestname
else
combined_properties = combined_properties + ", " + o.interestname
end
end
end
if shared_interest == true
matched_user_html = n.actualname + ": " + combined_properties
end
end
end
end
return matched_user_html
render :nothing => true
end
This returns an array of hashes with all users and their corresponding sharedproperties.
class User
def find_matching_users
returning Array.new do |matching_users|
self.logged_in.each do |other_user|
next if current_user == other_user # jump if current_user
# see http://ruby-doc.org/core/classes/Array.html#M002212 for more details on the & opreator
unless (common_properties = current_user.properties & other_user.properties).empty?
matching_users << { :user => other_user, :common_properties => common_properties }
end
end
end
end
end
In your view you can do something like this:
<%- current_user.find_matching_users.each do |matching_user| -%>
<%-# you can acccess the user with matching_user[:user] -%>
<%-# you can acccess the common properties with matching_user[:common_properties] -%>
<%- end -%>
You can use a hash table with the key being the user object and the value being an array of the shared properties . This is assuming that you first need to do a lookup based on the user .
Something like this :
#user_results = { user1 => [sharedproperty3,sharedproperty7] , user2 => [sharedproperty10,sharedproperty11,sharedproperty12]}
You can then acces the values like :
#user_results[user1]
or you can also iterate over all the keys using #user_results.keys

What's the most efficient way to keep a user database in sync with an external mailing list service?

I'd like some advice on how I should synchronize a list of email addresses on 11k users against an external mailing list program, in this case Mailchimp.
Normally the way I'd do this is simply to have an :after_save callback, to send a single update to the external api.
But already each hour, a rake task is run to update a property on every user in the database. If I simply did that, every hour, the the poor mailchimp API would get be hit 11,000 times.
What's the most efficient, simple way to do this, to check only if a single attribute you're watching has changed from what it was before the save?
If there's a variable that persists across the transaction lifecycle I would simply do something like this, where I check if the value has changed, and if it's different execute come other code.
class User
:before_save :store_old_email
:after_save :sync_with_chimp
def store_old_email
$ugly_of_global_variable_to_store_email = user.email
end
:sync_with_chimp
if $ugly_of_global_variable_to_store_email != user.email
//update_mail_chimp_api
end
end
end
I've checked the rails api here, and I'm still slightly unclear on how I should be doing this.
Would you use the dirty? class here to do this?
This is the way I went with in the end.
It turns out Rails gives you loads of handy callbacks in the dirty to do this.
Any suggestions on how to make this code less repetitive wold be gratefully received.
def update_mailchimp(optin)
# Create a Hominid object (A wrapper to the mailchimp api), and pass in a hash from the yaml file
# telling which mailing list id to update with subscribe/unsubscribe notifications)
#hominid = Hominid.new
client_site_list_id = YAML.load(File.read(RAILS_ROOT + "/config/mailchimp.yml"))
case optin
when 'subscribe_newsletter'
logger.debug("subscribing to newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_newsletter'
logger.debug("unsubscribing from newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'subscribe_monthly_update'
logger.debug("subscribing to monthly update...")
"success!" if #hominid.subscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_monthly_update'
logger.debug("unsubscribing from monthly update...")
"success!" if #hominid.unsubscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
end
end
# Keep the users in sync with mailchimp's own records - by only firing requests to the API if details on a user have changed after saving.
def check_against_mailchimp
logger.info("Checking if changes need to be sent to mailchimp...")
if newsletter_changed?
logger.info("Newsletter changed...")
newsletter ? update_mailchimp('subscribe_newsletter') : update_mailchimp('unsubscribe_newsletter')
end
if monthly_update_changed?
logger.info("update preferences changed...")
monthly_update ? update_mailchimp('subscribe_monthly_update') : update_mailchimp('unsubscribe_monthly_update')
end
end
you could change your users model to an active resource instead of active record and just use mailchimps api as your db for users
this is an older post about active resource but might get you started down the right path
http://www.therailsway.com/2007/9/3/using-activeresource-to-consume-web-services

How do I populate a table in rails from a fixture?

Quick summary:
I have a Rails app that is a personal checklist / to-do list. Basically, you can log in and manage your to-do list.
My Question:
When a user creates a new account, I want to populate their checklist with 20-30 default to-do items. I know I could say:
wash_the_car = ChecklistItem.new
wash_the_car.name = 'Wash and wax the Ford F650.'
wash_the_car.user = #new_user
wash_the_car.save!
...repeat 20 times...
However, I have 20 ChecklistItem rows to populate, so that would be 60 lines of very damp (aka not DRY) code. There's gotta be a better way.
So I want to use seed the ChecklistItems table from a YAML file when the account is created. The YAML file can have all of my ChecklistItem objects to be populated. When a new user is created -- bam! -- the preset to-do items are in their list.
How do I do this?
Thanks!
(PS: For those of you wondering WHY I am doing this: I am making a client login for my web design company. I have a set of 20 steps (first meeting, design, validate, test, etc.) that I go through with each web client. These 20 steps are the 20 checklist items that I want to populate for each new client. However, while everyone starts with the same 20 items, I normally customize the steps I'll take based on the project (and hence my vanilla to-do list implementation and desire to populate the rows programatically). If you have questions, I can explain further.
Just write a function:
def add_data(data, user)
wash_the_car = ChecklistItem.new
wash_the_car.name = data
wash_the_car.user = user
wash_the_car.save!
end
add_data('Wash and wax the Ford F650.', #user)
I agree with the other answerers suggesting you just do it in code. But it doesn't have to be as verbose as suggested. It's already a one liner if you want it to be:
#new_user.checklist_items.create! :name => 'Wash and wax the Ford F650.'
Throw that in a loop of items that you read from a file, or store in your class, or wherever:
class ChecklistItem < AR::Base
DEFAULTS = ['do one thing', 'do another']
...
end
class User < AR::Base
after_create :create_default_checklist_items
protected
def create_default_checklist_items
ChecklistItem::DEFAULTS.each do |x|
#new_user.checklist_items.create! :name => x
end
end
end
or if your items increase in complexity, replace the array of strings with an array of hashes...
# ChecklistItem...
DEFAULTS = [
{ :name => 'do one thing', :other_thing => 'asdf' },
{ :name => 'do another', :other_thing => 'jkl' },
]
# User.rb in after_create hook:
ChecklistItem::DEFAULTS.each do |x|
#new_user.checklist_items.create! x
end
But I'm not really suggesting you throw all the defaults in a constant inside ChecklistItem. I just described it that way so that you could see the structure of the Ruby object. Instead, throw them in a YAML file that you read in once and cache:
class ChecklistItem < AR::Base
def self.defaults
##defaults ||= YAML.read ...
end
end
Or if you wand administrators to be able to manage the default options on the fly, put them in the database:
class ChecklistItem < AR::Base
named_scope :defaults, :conditions => { :is_default => true }
end
# User.rb in after_create hook:
ChecklistItem.defaults.each do |x|
#new_user.checklist_items.create! :name => x.name
end
Lots of options.
A Rails Fixture is used to populate test-data for unit tests ; Dont think it's meant to be used in the scenario you mentioned.
I'd say just Extract a new method add_checklist_item and be done with it.
def on_user_create
add_checklist_item 'Wash and wax the Ford F650.', #user
# 19 more invocations to go
end
If you want more flexibility
def on_user_create( new_user_template_filename )
#read each line from file and call add_checklist_item
end
The file can be a simple text file where each line corresponds to a task description like "Wash and wax the Ford F650.". Should be pretty easy to write in Ruby,

Resources