Ruby 2.1.1 ArgumentError when passing hash before keyword arguments - keyword-argument

In Ruby 2.1.1 I defined the following method:
def post(url, params={}, send_json: false, success_only: true)
This is called within the same object in another method privateToken like so:
self.post("#{#url}/api/v3/session", {
:login => #user,
:password => #pass,
})['private_token']
However, calling privateToken in initialize for this object results in:
<script>:207:in `privateToken': unknown keywords: login, password (ArgumentError)
from <script>:199:in `initialize'
from <script>:575:in `new'
from <script>:575:in `<main>'
If I change the post method to accept params as a keyword argument then this error is avoided:
def post(url, params: {}, send_json: false, success_only: true)
# ..then in method 'privateToken':
self.post("#{#url}/api/v3/session", params: {
:login => #user,
:password => #pass,
})['private_token']
Could someone please explain why this happens? I haven't seen any mention of hash expansion to keywords in the Keyword arguments documentation. And from what I've read the argument ordering should be: standard arguments, default arguments, keyword arguments.

Try
def post(url, send_json: false, success_only: true, **params)
see this answer https://stackoverflow.com/a/20633975/1285164
now you can call
self.post("#{#url}/api/v3/session", login: #user, password: #pass)

Related

Ruby Strong Params: NoMethodError undefined method permit for Integer

My controller:
class V1::SendContractController < V1::BaseController
def create
byebug
bride_membership = Wedding.find(send_params[:weddingId]).bride_memberships[0]
SendBrideContractJob.perform_now(bride_membership, send_params[:contractId])
render json: { enqueuedDelivery: true }, status: :ok
end
private
def send_params
params
.require(:weddingId)
.permit(:contractId)
end
end
My params
Parameters: {"weddingId"=>4, "contractId"=>20, "send_contract"=>{"weddingId"=>4, "contractId"=>20}}
The error
NoMethodError (undefined method `permit' for 4:Integer):
But then when I byebug it I get what I want!
(byebug) params
<ActionController::Parameters {"weddingId"=>4, "contractId"=>20, "controller"=>"v1/send_contract", "action"=>"create", "send_contract"=>{"weddingId"=>4, "contractId"=>20}} permitted: false>
(byebug) params[:weddingId]
4
And I'm using axios with an interceptor to take care of formatting issues:
axios.interceptors.request.use((config) => {
if(config.url !== "/authentications") {
config.paramsSerializer = params => {
// Qs is already included in the Axios package
return qs.stringify(params, {
arrayFormat: "brackets",
encode: false
});
};
axios.defaults.headers.common['Authorization'] = `Bearer ${store.state.token.token}`
config.headers.common['Authorization']= `Bearer ${store.state.token.token}`
axios.defaults.headers.common['Accept'] = 'application/vnd.bella.v1+json'
config.headers.common['Accept'] = 'application/vnd.bella.v1+json'
return config
}
return config
})
I believe that require gives you the object at the key you provide to do further permit and / or require calls.
Perhaps you could try (not tested):
params.require(:weddingId)
params.permit(:weddingId, :contractId)
Edit: there's this too: Multiple require & permit strong parameters rails 4
Refer to this documentation and question.The require ensures that a parameter is present. If it's present, returns the parameter at the given key, otherwise raises an ActionController::ParameterMissing error.
p = { "weddingId"=>4, "contractId"=>20 }
ActionController::Parameters.new(p).require(:weddingId)
# 4
p = { "weddingId"=>nil, "contractId"=>20 }
ActionController::Parameters.new(p).require(:weddingId)
# ActionController::ParameterMissing: param is missing or the value is empty: weddingId
If you want to make sure :weddingId is present:
def contract_params
params.require(:weddingId)
params.permit(:contractId, :weddingId)
end
BTW, SendContractController is better called ContractsController.

PayPal express ActiveMerchant gateway not working

According to this, the ActiveMerchant PayPal Express Gateway is initialized like this:
paypal_options = {
login: "API_USERNAME_HERE",
password: "API_PASSWORD_HERE",
signature: "API_SIGNATURE_HERE"
}
::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options)
I'm definitely supplying a signature, yet I'm getting this error:
An API Certificate or API Signature is required to make requests to PayPal
The PayPal initializer looks like this (found here):
def initialize(options = {})
requires!(options, :login, :password)
headers = {'X-PP-AUTHORIZATION' => options.delete(:auth_signature), 'X-PAYPAL-MESSAGE-PROTOCOL' => 'SOAP11'} if options[:auth_signature]
options = {
:pem => pem_file,
:signature => signature,
:headers => headers || {}
}.update(options)
if options[:pem].blank? && options[:signature].blank?
raise ArgumentError, "An API Certificate or API Signature is required to make requests to PayPal"
end
super(options)
end
I don't understand what this initializer is doing with the signature and why it's not accepting it as per the example.
Here are the options I'm passing, which I've put to STDOUT:
{
"password" =>"***************",
"signature" =>"AVtrAKGQXoUNJFduUU0pn1dewq80AK9KYWenyFwYcduz8elS85B8T0Wc",
"allow_guest_checkout" =>true,
"login" =>"********************",
"test" =>true
}
Can someone help me with this please?
Note that I'm using this in JRuby, but I don't think that makes any difference in this case.
EDIT after #PiersC's comments:
I hardcoded this instead of taking them as params from Java and it worked:
options = {
login: "*************",
password: "*****************",
signature: "AVtrAKGQXoUNJFduUU0pn1dewq80AK9KYWenyFwYcduz8elS85B8T0Wc"
}
However this has led to another question. I've been converting the Java maps to Ruby hashes like this:
def self.convert_hash(map)
hsh = {}
map.each {|key, value| hsh[key] = value}
hsh.with_indifferent_access
end
And this has worked on all other gateways. How do I convert the Java map correctly to the options hash in Ruby?
Your option keys are strings but should be symbols, eg. { password: '***', ... } ActiveSupport::HashWithInvalidAccess hides (obscures?) the difference between symbol keys and string keys, but if you are using a regular Hash then { 'signature' => signature } is not the same as { signature: signature }.

How to share RSpec let variables between shared contexts?

I'm trying to DRY my RSpec request specs by using shared contexts. I'd like to share let variables between shared contexts so that they inherit and extend from one another.
Rspec.shared_context 'JSON request' do
let(:headers) do
{
'Accept' => 'application/json'
}
end
end
Rspec.shared_context 'Authenticated request' do
let(:headers) do
super().merge('Authorization' => "Bearer #{token}")
end
end
Rspec.describe 'user management' do
let(:token) { create(:oauth_token) }
include_context 'JSON request'
include_context 'Authenticated request'
it 'responds with a 200 ok' do
get '/user', headers: headers
expect(response).to have_http_status(:ok)
end
end
Declaring token works as expected, but using super() to override headers returns a NoMethodError suggesting super() is nil.
I'm not aware of a way to refer to the currently defined value of a let variable in a let block. (When I try it I get "stack level too deep".) I'd do what you're trying to do this way:
Rspec.shared_context 'JSON request' do
let(:common_headers) do
{
'Accept' => 'application/json'
}
end
let(:headers) { common_headers }
end
Rspec.shared_context 'Authenticated request' do
let(:headers) do
common_headers.merge('Authorization' => "Bearer #{token}")
end
end
You can use super() to get the value of a predefined let:
RSpec.shared_context 'common' do
let(:foo) { 'foo' }
end
RSpec.describe 'test' do
include_context 'common'
context 'reversed' do
let(:foo) { super().reverse }
it 'works' do
expect(foo).to eq('oof')
end
end
end

HTTParty options parameter not functioning properly

I'm setting up an application which can make LastFM API Requests.
These are simple get requests and I'm using the HTTParty gem.
My function is as follows:
def get_albums
self.class.base_uri "http://ws.audioscrobbler.com/2.0/"
options = {
:user => "Gerard1992",
:method => "user.gettopalbums",
:api_key => Constants::LASTFM_API_KEY,
:format => "json"
}
puts options.to_query
self.class.get "/?#{options.to_query}", {} #options don't work
end
This piece of code that's shown above works. The get request returns a set of JSON. My problem is that this /?#{options.to_query} doesn't look that neat. And neither does the actual (now empty {}) options parameter. How do I get the HTTParty options parameter to work like it should?
This is what I've tried, but both cases failed:
self.class.get "/", options
self.class.get "/", options => options
I appreciate the help.
The correct option for query parameters in HTTParty is :query, so what you want is:
self.class.get "/", query: options
You can see all the available parameters in the docs.
Send :verify => false in options hash

Rspec validation test fails on 'put' update

I have this factory: #host = FactoryGirl.create(:host)
This test passes:
it 'should update and redirect to the show page' do
new_attr = #host.attributes.merge("hostname" => "New_name")
put 'update', id: #host, host: new_attr
response.should redirect_to(host_path(#host))
endĀ¬
But this one fails:
it 'should fail on validation for empty hostname and redirect to edit' do
bad_attr = #host.attributes.merge("hostname" => "")
put 'update', id: #host, host: bad_attr
response.should redirect_to(edit_host_path(#host))
end
With this error:
1) HostsController POST update failure should redirect to edit
Failure/Error: response.should redirect_to(edit_host_path(#host))
Expected response to be a redirect to <http://test.host/hosts/1/edit> but was a redirect to <http://test.host/hosts/1>
# ./spec/controllers/hosts_controller_spec.rb:127:in `block (4 levels) in <top (required)>'
In the #update of my HostsController, host objects that fail validation with empty hostnames are supposed to redirect_to edit_host_path(#host). Here is my HostsController#update
def update
#host = Host.find(params[:id])
if #host.save
flash[:notice] = 'Host was successfully updated.'
redirect_to host_path(#host)
elseĀ¬
redirect_to edit_host_path(#host)
end
end
I've played with saving and updating attributes in the console and my validations work. So why this error? Any ideas? Rspec seems to be saying that my factory objects with bad attributes are passing validation, but my model validates fine. Thank you.
/me does a face-palm.
In HostsController#update, this:
#host = Host.find(params[:id])
#host.save
Should be this:
#host = Host.find(params[:id])
#host.update_attributes(params[:host])

Resources