creating multiple records with a single JSON post - ruby-on-rails

The following parameters are being posted
Parameters: {
"orig_id"=>47,
"terms_accepted"=>true,
"email"=>"sod#gos.co",
"name"=>"firstname",
"surname"=>"surname",
"kids"=>[
{"school"=>"Faraway", "rate"=> "89"},
{"school"=>"Transfer", "rate"=> "23"},
{"school"=>"Bike", "rate"=>"4"}]
}
However, the rails controller action, defined as follows only creates the parent record, but not the related ones:
parent = params[:parent]
#parent = Parent.new(orig_id: parent['orig_id'], terms_accepted: parent['terms_accepted'], email: parent['email'], name: parent['name'], surname: parent['surname'])
#parent.save
kids = params[:kids]
kids each do |kid|
#kid = Kid.new(school: kid['school'], rate: kid['rate'], parent_id: #parent.id)
#kid.save
end
where is the syntax wrong?

kids each do |kid| actually should be kids.each do |kid|.

Related

Add fields to model without permit param

am trying to add fields to a model directly from the controller action without a form,only the user_id is saved the other columns (firstname,lastname) are empty each time i run the code, below is the code, note: User has_many :provide_helps.
#firstname=current_user.firstname
#lastname=current_user.lastname
#gh_user = User.find_by status: 'gh'
#ph = #gh_user.provide_helps.create(firstname: "#{#firstname}" , lastname: "#
{#lastname}")
Try this
#ph = #gh_user.provide_helps.create(firstname: #firstname , lastname: #lastname)
No need to assign #first_name and #last_name using #{}.
you can use the code below:
#gh_user = User.find_by(status: 'gh')
#ph = #gh_user.provide_helps.new({ firstname: current_user.firstname, lastname: current_user.lastname })
#ph.save
or
#gh_user = User.find_by(status: 'gh')
#ph = #gh_user.provide_helps.new()
#ph.first_name = current_user.first_name
#ph.last_name = current_user.last_name
#ph.save

Create a method to set attributes after initializing an object

I'm initializing a new object and setting the attributes (because there are no attributes for this particular object) before rendering a form like so:
def new
Book.new title: nil, author: nil, genre: nil, language: nil, ect...
end
This to me looks like a code smell.
I'm trying to set the attributes in a method within the model so I can increase readability by using: Book.new.set_attributes. So my set_attributes method in the Book model would look like:
def set_attributes
{posted: nil, company: nil, poster: nil, city: nil, state: nil, title: nil, body: nil, keywords: nil}
end
However this does not work (with or without the {} brackets). Is it possible to call a method after using .new?
Ruby's constructor method is initialize, not new. You shouldn't try to define a method called new. Do something like:
class Book
attr_accessor :title, :author
def initialize(title = nil, author = nil)
#title = title
#author = author
end
end
You don't need to initialize nil values. When calling Book.new, any values that are not provided in a hash (e.g., Book.new(title: 'Bozo', author: 'Clown')) will be nil automatically.

Understanding how to test a class using RSpec

The main thing I am looking to achieve from this question is understanding. With some assistance I have been looking at refactoring my controller code into more manageable modules/classes so that I can test them effectively. I have an example here that I would like to work on, my question is how would I test the class Sale:
class TransactionsController < ApplicationController
def create
payment = BraintreeTransaction::VerifyPayment.new(params, #user_id, #transaction_total)
payment.run(params)
if payment.success?
redirect_to thank_you_path
else
flash.now[:alert] = payment.error
flash.keep
redirect_to new_transaction_path
end
end
module BraintreeTransaction
class VerifyPayment
def initialize(params, user_id, total)
#transaction_total = total
#user_id = user_id
#params = params
#error_message = nil
end
def run(params)
#result = BraintreeTransaction::Sale.new.braintree_hash(params, #transaction_total)
if #result.success?
#cart_items = CartItem.where(user_id: #user_id).where.not(image_id: nil)
#cart_items.destroy_all
create_real_user
update_completed_transaction
guest_user.destroy
#success = true
else
update_transaction
#error_message = BraintreeErrors::Errors.new.error_message(#result)
end
end
def success?
#success
end
def error
#error_message
end
end
module BraintreeTransaction
class Sale
def braintree_hash(params, total)
Braintree::Transaction.sale(
amount: total,
payment_method_nonce: params[:payment_method_nonce],
device_data: params[:device_data],
customer: {
first_name: params[:first_name],
last_name: params[:last_name],
email: params[:email],
phone: params[:phone]
},
billing: {
first_name: params[:first_name],
last_name: params[:last_name],
company: params[:company],
street_address: params[:street_address],
locality: params[:locality],
region: params[:region],
postal_code: params[:postal_code]
},
shipping: {
first_name: params[:shipping_first_name].presence || params[:first_name].presence,
last_name: params[:shipping_last_name].presence || params[:last_name].presence,
company: params[:shipping_company].presence || params[:company].presence,
street_address: params[:shipping_street_address].presence || params[:street_address].presence,
locality: params[:shipping_locality].presence || params[:locality].presence,
region: params[:shipping_region].presence || params[:region].presence,
postal_code: params[:shipping_postal_code].presence || params[:postal_code].presence
},
options: {
submit_for_settlement: true,
store_in_vault_on_success: true
}
)
end
end
end
I don't know if I am looking at this wrong but this piece of code here BraintreeTransaction::Sale.new.braintree_hash is what I want to test and I want to ensure that when called the class receives a hash ?
Update
So far I have come up with this (though I am not 100% confident it is the correct approach ?)
require 'rails_helper'
RSpec.describe BraintreeTransaction::Sale do
#transaction_total = 100
let(:params) { FactoryGirl.attributes_for(:braintree_transaction, amount: #transaction_total) }
it 'recieves a hash when creating a payment' do
expect_any_instance_of(BraintreeTransaction::Sale).to receive(:braintree_hash).with(params, #transaction_total).and_return(true)
end
end
I get an error returned which I don't understand
Failure/Error: DEFAULT_FAILURE_NOTIFIER = lambda { |failure, _opts| raise failure }
Exactly one instance should have received the following message(s) but didn't: braintree_hash
I might not be spot on but I would answer the way I would have tackled the issue. There are three ways you can write a test that hits the code you want to test.
Write a unit test for braintree_hash for BraintreeTransaction::Sale object
Write a controller unit method for create method in TransactionsController controller
write an integration test for route for create method in TransactionsController.
These are the ways you can start exploring.
A couple of things here. All the suggestions for refactoring your code (from your other question Writing valuable controller tests - Rspec) apply here. I can make further suggestions on this code, if helpful.
In your test, I believe your problem is that you never actually call BraintreeTransaction.new.braintree_hash(params) (which should be called immediately following your expect_any_instance_of declaration). And so no instances ever receive the message(s).

Create or Update Rails 4 - updates but also creates (Refactoring)

In my Rails API I have the following code in my Child model:
before_create :delete_error_from_values, :check_errors, :update_child_if_exists
def delete_error_from_values
#new_error = self.values["error"]
#values = self.values.tap { |hs| hs.delete("error") }
end
def update_child_if_exists
conditions = {type: self.type, parent_id: self.parent_id}
if existing_child = Child.find_by(conditions)
new_values = existing_child.values.reverse_merge!(#values)
hash = {:values => new_values}
existing_child.update_attributes(hash)
end
end
def check_errors
if self.type == "error"
conditions = {type: self.type, parent_id: self.parent_id}
if existing_child = Child.find_by(conditions)
bd_errors = existing_child.error_summary
bd_errors[#new_error] = bd_errors[#new_error].to_i + 1
hash = {:error_summary => bd_errors}
existing_child.update_attributes(hash)
else
self.error_summary = {#new_error => 1}
end
end
end
This works like expected, except for one small detail: The Child is updated if a record by type and parent_id already exists, but it is also created. How can I refactor this to stop creation?
I've tried to include return false, but if I do this, the update is not successful.
I wish to have something like find_or_create_by, but I'm not sure how to use it for this cases.
May be you can refactor your code in following approach:
def create
#parent = Parent.find(params[:parent_id])
existing_child = Child.where(type: child_params[:type], parent_id:
child_params[:parent_id]).first
if existing_child.present?
existing_child.update_attributes(attribute: value_1)
else
#child = #parent.child.build(child_params)
end
#other saving related code goes here.
end
This is just a basic piece of example.
Try creating separate instance methods to keep the Contrller DRY. :)

Ruby on Rails: Updating data problems

Okay so I'm just trying to write some simple code that updates a record given an ID. This is what it looks like.
def updaterecord
bathroom = Bathroom.find(params[:key])
bathroom.name= params[:name],
#bathroom.bathroomtype = params[:bathroomtype],
bathroom.street = params[:street]
#bathroom.city = params[:city],
#bathroom.state = params[:state],
#bathroom.country = params[:country],
#bathroom.postal = params[:postal],
#bathroom.access = params[:access],
#bathroom.directions = params[:directions],
#bathroom.comment = params[:commment],
#bathroom.avail = params[:avail]
bathroom.save
end
The problem is that although I am trying to update each individual attribute they are all getting concatenated to the name field. For example, this code above sets the name field to the name plus the address. I don't know why?
This is what the console looks like if I try to query after doing the update.
Bathroom Load (0.2ms) SELECT "bathrooms".* FROM "bathrooms" WHERE "bathrooms"."ID" = ? LIMIT 1 [["ID", 4017]]
=> #<Bathroom ID: 4017, name: "--- \n- ttyt\n- 113 some\n", bathroomtype: "0", street: "113 some", city: "Portland", state: "OR", country: "United States", postal: "97217", lat: #<BigDecimal:1109f2890,'0.4558056E2',12(12)>, lon: #<BigDecimal:1109f27c8,'-0.122677857E3',12(16)>, access: "0", directions: "", comment: nil, created: "2012-06-08 17:19:03.420329", modifed: "", avail: "1", slug: "", source: "Squat">
And this is what the post values look like:
post values = key=4017&name=www&bathroomtype=0&street=7540 N Interstate Ave&city=Portland&state=OR&country=United States&postal=97217&access=0&directions=&comment=<null>&avail=1
Why can't I get each field to update individually? Sorry I'm confused as to what is going on?
I think you might an unnecessary comma there at the end of the line.
It should just read:
def updaterecord
bathroom = Bathroom.find(params[:key])
bathroom.name= params[:name]
bathroom.street = params[:street]
bathroom.save
end
it doesn't look like you have correctly urlencoded your post values
a simple
puts params.inspect
or
pp params
should log out the params object. You can also use
render :text => params.inspect
to print it out to your result html

Resources