Passing instance variables into API in rails - ruby-on-rails

I'm trying to pass in some instance variables to call an API with that specific object's attributes. A user fills in their car details (make, model, and year) which creates an offer object. That is supposed to be passed into Edmund's API to retrieve the info for that car. The code works fine if I set it with a specific make/model/year but I can't make it return info for a created offer object.
Here's my controller:
def show
#offer = Offer.find(params[:id])
#wanted_ad = WantedAd.find(params[:wanted_ad_id])
#make = #offer.ownermake
#model = #offer.ownermodel
#year = #offer.owneryear
respond_to do |format|
format.html # show.html.erb
format.json { render json: #offer }
end
end
And here's my model:
class Offer < ActiveRecord::Base
attr_accessible :user_id, :wanted_ad_id, :estvalue, :image1, :offerprice, :ownercartype, :ownerdesc, :ownermake, :ownermileage, :ownermodel, :owneryear
belongs_to :user
belongs_to :wanted_ad
has_one :car
def self.carsearch
#car = []
carinfo = HTTParty.get("http://api.edmunds.com/v1/api/vehicle/#{make}/#{model}/#{year}?api_key=qd4n48eua7r2e59hbdte5xd6&fmt=json")
carinfo["modelYearHolder"].each do |p|
c = Car.new
c.make = p["makeName"]
return carinfo
end
end
end
My car model is simply:
class Car < ActiveRecord::Base
attr_accessible :make, :model, :year
belongs_to :offer
end
And I'm trying to call it from a view file with <%= Offer.carsearch %>. I'm probably all sorts of messed up but this is my first time working with an API and I'm very lost.

I think you got several logical errors in your carsearch method:
You're fetching a carinfo, iterate through an array, instantiate a new car but nothing happens with the c object and at the end of the first iteration you exit the whole function returning the retrieved carinfo...
Is this probably what you've meant?
def carsearch
#cars = []
# where do `make`, `model` and `year` come from here?
# probably method parameters!?
carinfo = HTTParty.get("http://api.edmunds.com/v1/api/vehicle/#{make}/#{model}/#{year}?api_key=qd4n48eua7r2e59hbdte5xd6&fmt=json")
carinfo["modelYearHolder"].each do |p|
c = Car.new
c.make = p["makeName"]
# initialize other attributes (year, model)?
#cars << c
end
return #cars
end

Related

rails 4 simple form nested attributes multiple models error during update

I am battling an error with nested attributes and trying to fix the cop error at the same time. So here is the walk through. A coupon code may be submitted with the form using nested attributes that may affect the price of the job. This only occurs if the coupon code is valid. In this scenario the coupon code has already been assigned so the first if coupon_code && coupon.nil? is triggered. When the form comes back around the flash message works correctly but simple form does not display the value. I could adjust simple form to have the value with an instance variable but I'm starting to smell something a bit off here in my logic. Also, the smell of Assignment Branch Condition is starting to worry me. I can move forward with this, but the user would like to see the code. I would too.
Cop Error:
app/controllers/payments_controller.rb:9:3: C: Assignment Branch Condition size for update is too high. [17.97/15]
Controller:
class PaymentsController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :route_not_found_error
Numeric.include CoreExtensions::Numeric::Percentage
def update
#job = Job.find(params[:job_id])
coupon_code = params[:job][:coupon_attributes][:code]
coupon = validate_coupon(coupon_code)
if coupon_code && coupon.nil?
#coupon_code = coupon_code
flash.now[:error] = t('flash_messages.coupons.id.not_found')
render 'payments/new', layout: 'nested/job/payment'
else
update_job(#job, coupon)
update_coupon(coupon, #job) if coupon
redirect_to #job.vanity_url
end
end
def new
#job = Job.find(params[:job_id])
return if reroute?(#job)
render 'payments/new', layout: 'nested/job/payment'
end
private
def update_job(job, coupon)
job.start_at = DateTime.now
job.end_at = AppConfig.product['settings']['job_active_for_day_num'].days.from_now
job.paid_at = DateTime.now
job.price = price_job(coupon)
# job.save
end
def validate_coupon(coupon_code)
return nil unless coupon_code.present?
coupon = Coupon.active.find_by_code(coupon_code)
return nil unless coupon.present?
coupon
end
def price_job(coupon)
price = AppConfig.product['settings']['job_base_price']
return price unless coupon
price = coupon.percent_discount.percent_of(price)
price
end
def update_coupon(coupon, job)
coupon.job_id = job.id
coupon.executed_at = DateTime.now
coupon.save
end
end
View:
ruby:
content_for :body_id_class, 'PaymentNew'
content_for :js_instance, 'viewPaymentNew'
content_for :browser_title, 'Payment'
job_base_price = AppConfig.product['settings']['job_base_price']
coupon_code = #coupon_code ||= ''
= simple_form_for(#job, url: job_payment_path, html: { id: 'payment-processor-form' }) do |j|
div[class='row']
div[class='col-md-12']
div[class='panel panel-default']
div[class='panel-heading']
h3[class='panel-title']
|Total Cost
div[class='panel-body']
h2[class='job-cost' data-initial = "#{job_base_price}"]
= number_to_currency(job_base_price)
div[class='panel-heading']
h3[class='panel-title']
|Have a coupon?
div[class='panel-body']
div[class='row-inline']
div[class='row-block row-block-one']
= j.simple_fields_for :coupon_attributes, #job.coupon do |c|
= c.input_field :code, maxlength: 50, id: 'coupon-code', class: 'form-control', data: { 'initial' => 0 }, value: coupon_code
div[class='row-block']
button[type='button' class='btn btn-primary' id='coupon-verify' ]
|Verify
p[class='help-hint']
= t('simple_form.hints.coupon.code')
div[class='row']
div[class='col-md-12']
= j.button :button, type: 'button', class: 'btn-primary text-uppercase', id: 'purchase-job' do
= job_posting_button_step_label
Updates
Refactoring this code to work with the post below. Factories fixed factorygirl create model association NoMethodError: undefined method
You have quite a few code smells going on in that fat old controller.
Most of them seem to be symtoms that all is not well on the model layer and that you are not modeling the domain very well.
You might want to consider something like this:
class Job < ActiveRecord::Base
has_many :payments
end
class Payment < ActiveRecord::Base
belongs_to :job
belongs_to :coupon
end
class Coupon < ActiveRecord::Base
validates_uniqueness_of :code
end
This will let our countroller focus on CRUD'ing a single resouce rather than trying to herd a bunch of cats.
So lets look at enforcing the business logic for coupons.
class Payment < ActiveRecord::Base
belongs_to :job
belongs_to :coupon
validate :coupon_must_be_active
attr_writer :coupon_code
def coupon_code=(code)
coupon = Coupon.find_by(code: code)
#coupon_code = code
end
private
def coupon_must_be_active
if coupon
errors[:coupon] << "must be active." unless coupon.active?
elsif #coupon_code.present?
errors[:coupon_code] << "is not valid."
end
end
end
The custom attribute writer loads the coupon from the a code. The validation sets up our business logic rules.
We really should do the same when it comes to the job pricing:
class Job < ActiveRecord::Base
after_initialize :set_price
def set_price
self.price ||= AppConfig.product['settings']['job_base_price']
end
end
class Payment < ActiveRecord::Base
after_initialize :set_price
validates_presence_of :job
def net_price
return job.price unless coupon
job.price * (coupon.percent_discount * 00.1)
end
# ...
end
We can then write our controller like so:
class PaymentsController
before_action :set_job
# GET /jobs/:job_id/payments/new
def new
#payment = #job.payments.new
end
# POST /jobs/:job_id/payments
def create
#payment = #job.payments.create(payment_params)
end
# PATCH /jobs/:job_id/payments/:id
def update
#payment = #job.payments.find(params[:id])
end
private
def set_job
#job = Job.find(params[:job_id])
end
def payment_params
params.require(:payment)
.permit(:coupon_code)
end
end
We can then simply setup the form with:
= simple_form_for([#job, #payment]) do |f|
= f.input :coupon_code
= f.submit
Note that you don't want to take the price from the user unless you intend to implement the honor system - you should get it from your models by setting up association callbacks.

Rails 4: Iterate through an array to create model in controller

I pass parameters from a view to a controller. The parameter is an array that is generated by the user. The user can add as many items to the array as they want. I want to iterate through this array to create multiple model objects in the DB. How can I go about doing this?
A person can create a meal, and within the meal form, there are options to add as many food items as they wish.
def create
#meal= Meal.new(question_params)
food_options = params[:food_options]
i = 0
if #meal.save
food_options.each do |x|
#meal.foods.Create(:drink => food_option[i], :meal => #meal)
i = +1
end
redirect_to #meal
else
render 'new'
end
end
Any guidance would be much appreciated
Use accepts_nested_attributes_for and let Rails handle it for you.
In the models/meal.rb
class Meal < ActiveRecord::Base
has_many :foods
accepts_nested_attributes_for :foods # <==========
...
end
and in the controller, include the nested attributes:
class MealsController < ApplicationController
...
def create
#meal= Meal.new(question_params)
redirect_to #meals
else
render 'new'
end
...
def question_params
params.require(:meal).permit(...., foods_attributes: [ :drink, .... ]) # <====
end
end

Save external Tweets in database in Rails

I am new to rails developement and to the MVC architecture. I have a little application where I can add Videos' URLs from Dailymotion or Youtube and get the tweets related to that URL using the twitter gem in Ruby on Rails.
Now i'm able to store the tweets like this : (This is the video controller)
def show
#video = Video.find(params[:id])
# Creating a URL variable
url = #video.url
# Search tweets for the given video/url
#search = get_client.search("#{#video.url} -rt")
# Save tweets in database
#search.collect do |t|
tweet = Tweet.create do |u|
u.from_user = t.user.screen_name.to_s
u.from_user_id_str = t.id.to_s
u.profile_image_url = t.user.profile_image_url.to_s
u.text = t.text.to_s
u.twitter_created_at = t.created_at.to_s
end
end
I'm not sure if this is the right way to do it (doing it in the controller ?), and what I want to do now is to specify that those tweets that have just been stored belong to the current video. Also I would like to have some sort of validation that makes the controller look in the database before doing this to only save the new tweets. Can someone help me with that ?
My models :
class Video < ActiveRecord::Base
attr_accessible :url
has_many :tweets
end
class Tweet < ActiveRecord::Base
belongs_to :video
end
My routes.rb
resources :videos do
resources :tweets
end
This is an example of a "fat controller", an antipattern in any MVC architecture (here's a good read on the topic).
Have you considered introducing a few new objects to encapsulate this behavior? For example, I might do something like this:
# app/models/twitter_search.rb
class TwitterSearch
def initialize(url)
#url = url
end
def results
get_client.search("#{#url} -rt")
end
end
# app/models/twitter_persistence.rb
class TwitterPersistence
def self.persist(results)
results.map do |result|
self.new(result).persist
end
end
def initialize(result)
#result = result
end
def persist
Tweet.find_or_create_by(remote_id: id) do |tweet|
tweet.from_user = screen_name
tweet.from_user_id_str = from_user_id
tweet.profile_image_url = profile_image_url
tweet.text = text
tweet.twitter_created_at = created_at
end
end
private
attr_reader :result
delegate :screen_name, :profile_image_url, to: :user
delegate :id, :user, :from_user_id, :text, :created_at, to: :result
end
Notice the use of find_or_create_by ... Twitter results should have a unique identifier that you can use to guarantee that you don't create duplicates. This means you'll need a remote_id or something on your tweets table, and of course I just guessed at the attribute name (id) that the service you're using will return.
Then, in your controller:
# app/controllers/videos_controller.rb
class VideosController < ApplicationController
def show
#tweets = TwitterPersistence.persist(search.results)
end
private
def search
#search ||= TwitterSearch.new(video.url)
end
def video
#video ||= Video.find(params[:id])
end
end
Also note that I've removed calls to to_s ... ActiveRecord should automatically convert attributes to the correct types before saving them to the database.
Hope this helps!

ActiveRecord::RecordNotFound - in a descendant class' associated_controller#index

I am attempting to locate a parent object in a nested controller, so that I can associate the descendant resource with the parent like so:
# teams_controller.rb <snippet only>
def index
#university = Univeresity.find(params[:university_id])
#teams = #university.teams
end
When I call find(params[:university_id]) per the snippet above & in line 6 of teams_controller.rb, I receive ActiveRecord::RecordNotFound - Couldn't find University without an ID.
I'm not only interested in fixing this issue, but would also enjoy a better understanding of finding objects without having to enter a University.find(1) value, since I grant Admin the privilege of adding universities.
The Rails Guides say the following about the two kinds of parameters in a website:
3 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of
parameters possible in a web application. The first are parameters
that are sent as part of the URL, called query string parameters. The
query string is everything after “?” in the URL. The second type of
parameter is usually referred to as POST data. This information
usually comes from an HTML form which has been filled in by the user.
It’s called POST data because it can only be sent as part of an HTTP
POST request. Rails does not make any distinction between query string
parameters and POST parameters, and both are available in the params
hash in your controller:
It continues a little further down, explaining that the params hash is an instance of HashWithIndifferentAccess, which allows usage of both symbols and strings interchangeably for the keys.
From what I read above, my understanding is that Rails recognizes both parameters (URL & POST) and stores them in the same hash (params).
Can I pass the params hash into a find method in any controller action, or just the create/update actions? I'd also be interested in finding a readable/viewable resource to understand the update_attributes method thats called in a controller's 'update' action.
Please overlook the commented out code, as I am actively searching for answers as well.
Thanks in advance.
Here are the associated files and server log.
Webrick
teams_controller.rb
class TeamsController < ApplicationController
# before_filter :get_university
# before_filter :get_team
def index
#university = University.find(params[:univeristy_id])
#teams = #university.teams
end
def new
#university = University.find(params[:university_id])
#team = #university.teams.build
end
def create
#university = University.find(params[:university_id])
#team = #university.teams.build(params[:team])
if #team.save
redirect_to [#university, #team], success: 'Team created!'
else
render :new, error: 'There was an error processing your team'
end
end
def show
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def edit
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def update
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
if #team.update_attributes(params[:team])
redirect_to([#university, #team], success: 'Team successfully updated')
else
render(:edit, error: 'There was an error updating your team')
end
end
def destroy
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
#team.destroy
redirect_to university_teams_path(#university)
end
private
def get_university
#university = University.find(params[:university_id]) # can't find object without id
end
def get_team
#team = #university.teams.find(params[:id])
end
end
team.rb
class Team < ActiveRecord::Base
attr_accessible :name, :sport_type, :university_id
has_many :home_events, foreign_key: :home_team_id, class_name: 'Event'
has_many :away_events, foreign_key: :away_team_id, class_name: 'Event'
has_many :medias, as: :mediable
belongs_to :university
validates_presence_of :name, :sport_type
# scope :by_university, ->(university_id) { where(team_id: team_id).order(name: name) }
# scope :find_team, -> { Team.find_by id: id }
# scope :by_sport_type, ->(sport_type) { Team.where(sport_type: sport_type) }
# scope :with_university, joins: :teams
# def self.by_university(university_id)
# University.where(id: 1)
# University.joins(:teams).where(teams: { name: name })
# end
def self.by_university
University.where(university_id: university_id).first
end
def self.university_join
University.joins(:teams)
end
def self.by_sport_type(sport_type)
where(sport_type: sport_type)
end
def self.baseball
by_sport_type('Baseball/Softball')
end
end
university.rb
class University < ActiveRecord::Base
attr_accessible :address, :city, :name, :state, :url, :zip
has_many :teams, dependent: :destroy
validates :zip, presence: true, format: { with: /\A\d{5}(-\d+)?\z/ },
length: { minimum: 5 }
validates_presence_of :name, :address, :city, :state, :url
scope :universities, -> { University.order(name: 'ASC') }
# scope :by_teams, ->(university_id) { Team.find_by_university_id(university_id) }
# scope :team_by_university, ->(team_id) { where(team_id: team_id).order(name: name)}
def sport_type
team.sport_type
end
end
views/teams/index.html.erb
Placed in gists for formatting reasons
rake routes output: (in a public gist)
enter link description here
rails console
You're not going to want to have both:
resources :universities #lose this one
resources :universities do
resources :teams
end
As for params... you have to give a param. So, when you go to http://localhost:3000/teams there are no params, by default. If you go to http://localhost:3000/teams/3 then params[:id] = 3 and this will pull up your third team.
Keep in mind the nomenclature of an index. The index action of Teams, is going to list all of the teams. All of them. There is no one University there, so what are you actually trying to find? If anything, you'd have, for your University controller:
def show
#university = University.find(params[:id])
#teams = #university.teams
end
so, the address bar will be showing http://localhost:3000/universities/23, right? params[:id] = 23, then you can find the teams associated with that university.

Rails 3: alias_method_chain to set specific attribute first

When user's create a post I'd like to set the user_id attribute first. I'm trying to do this using alias_method_chain on the arrtibutes method. But I'm not sure if this is right as the problem I thought this would fix is still occurring. Is this correct?
Edit:
When my users create a post they assign 'artist(s)' to belong to each post, using a virtual attribute called 'artist_tokens'. I store the relationships in an artist model and a joined table of artist_ids and post_ids called artisanships.
I'd like to to also store the user_id of whomever created the artist that belongs to their post (and I want it inside the artist model itself), so I have a user_id column on the artist model.
The problem is when I create the artist for each post and try to insert the user_id of the post creator, the user_id keeps showing as NULL. Which is highly likely because the post's user_id attribute hasn't been set yet.
I figured to get around this I needed to set the user_id attribute of the post first, then let the rest of the attributes be set as they normally are. This is where I found alias_method_chain.
post.rb
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1, :user_id => self.user_id).id
end
self.artist_ids = ids.split(",")
end
def attributes_with_user_id_first=(attributes = {})
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
EDIT:
class ArtistsController < ApplicationController
def index
#artists = Artist.where("name like ?", "%#{params[:q]}%")
results = #artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
In your controller, why not just do this:
def create
#post = Post.new :user_id => params[:post][:user_id]
#post.update_attributes params[:post]
...
end
But it seems to me that it would be much better to create the artist records after you've done validation on the post rather than when you first assign the attribute.
EDIT
I would change this to a callback like this:
class Post < ActiveRecord::Base
attr_accessor :author_tokens
def artist_tokens=(tokens)
#artist_tokens = tokens.split(',')
end
after_save :create_artists
def create_artists
#artist_tokens.each do |token|
...
end
end
end

Resources