HTTParty Google maps directions between origin and destination. - ruby-on-rails

My goal: http://maps.googleapis.com/maps/api/directions/json?origin=montreal&destination=toronto&sensor=false
My class:
class GoogleMap
include HTTParty
base_uri 'http://maps.googleapis.com'
attr_accessor :origin, :destination
def initialize(service, page)
#options = { query: {origin: origin, destination: destination} }
end
def directions
self.class.get("/maps/api/directions/json", #options)
end
end
Currently when I run this on the console:
irb(main):001:0> g = GoogleMap.new("montreal", "toronto")
=> #<GoogleMap:0x007fcaeeb88538 #options={:query=>{:origin=>nil, :destination=>nil}}>
irb(main):002:0> g.directions
=> #<HTTParty::Response:0x7fcaeeb60b00 parsed_response={"error_message"=>"Invalid request. Missing the 'origin' parameter.", "routes"=>[]...
Problem is: {:query=>{:origin=>nil, :destination=>nil}} origin and destination are nil.
I would like to know how I would achieve:
irb(main):001:0> g = GoogleMap.new("montreal", "toronto")
=> #<GoogleMap:0x007fcaeeb88538 #options={:query=>{:origin=>montreal, :destination=>toronto}}
And then when I run:
g.directions I get output of http://maps.googleapis.com/maps/api/directions/json?origin=montreal&destination=toronto&sensor=false
Thank you in advance.

I think you might want to change your
def initialize(service, page)
to
def initialize(origin, destination)
Or you can do g.origin = "montreal" and g.destination = "toronto" before you call g.directions

Related

Trigger rails controller function - Paypal Website Standard IPN

I've got a Paypal IPN that comes into a PaymentNotificationsController in my app. However, some variables depend on the number of items in a cart, so i want to extract them before creating the PaymentNotification.
So far, i've got:
class PaymentNotificationsController < ApplicationController
protect_from_forgery except: [:create]
def create
PaymentNotification.create!(params: params,
item_number: params[:item_number], item_name: params[:item_name], quantity: params[:quantity]
render nothing: true
end
end
However, when the notification comes from PayPal, it comes in the form of item_name1, item_number1, quantity1, item_name2, item_number2, quantity2 and so on.
Even if its just one item, it would come as item_name1, item_number1, quantity1, option1 and so on.
I have this function to try and extract the variables, but i don't know how to trigger the function. I tried using a before_action at the top of the controller but it didn't work. Returned wrong number of arguments(0 for 1):
ITEM_PARAM_PREFIXES = ["item_name", "item_number", "quantity"]
def extract_ipn_items_params(params)
item_params = []
loop do
item_num_to_test = item_params.length + 1
item_num_suffix = item_num_to_test.to_s
possible_param_name = ITEM_PARAM_PREFIXES[0] + item_num_suffix
if params.include?(possible_param_name)
this_item_params = {}
ITEM_PARAM_PREFIXES.each do |prefix|
this_item_params[prefix] = params[prefix + item_num_suffix]
end
item_params.push this_item_params
else
return item_params
end
end
end
So i'm asking, how do i trigger the function to extract the variables and put them into params[:item_number], params[:item_name], params[:quantity] for each item in the cart so if there are two items, two separate PaymentNotifications would be created?
Note: Both methods are in the same PaymentNotificationsController.
Any help would be appreciated. Thanks in advance!
I assume your method extract_ipn_items_params already fetches the data you require, you can remove the params argument to the method, as the params is always available in the actions/methods of the controller.
ITEM_PARAM_PREFIXES = ["item_name", "item_number", "quantity"]
def extract_ipn_items_params
mod_params = Hash.new{|k, v| k[v] = {} }
ITEM_PARAM_PREFIXES.each do |item_data_key|
key_tracker = 1
loop do
current_key = (item_data_key + key_tracker.to_s).to_sym
if params.include? current_key
mod_params[key_tracker][item_data_key] = params[current_key]
else
break
end
key_tracker += 1
end
end
mod_params
end
The method returns a hash of hashes like:
{1 => {item_name: 'Item 1', item_number: 1084, quantity: 15}}, if you have nested attributes set up for a user, I think you should be able to do something like, not really sure, but should be possible:
user.update(payment_notifications_attributes: extract_ipn_items_params)
Let me know if that works for you.
UPDATE
Based on the Github Gist, here's something I was able to come up with:
class PaymentNotificationsController < ApplicationController
protect_from_forgery except: [:create]
ITEM_PARAM_PREFIXES = ["item_name", "item_number", "quantity", "option_name"]
def create
extract_ipn_items_params.each do |key, values|
# this approach loops through all the returned results, nested attributes may help abstract this though
PaymentNotification.create(values)
render nothing: true
end
def details
# params.extract_ipn_items_params #this doesn't exist as params is an instance of ActionController::Parameters
PaymentNotification.update_attributes(line_item_id: params[:item_number], product_title: params[:item_name], option_name: params[:option_name], quantity: params[:quantity])
end
private
def additional_attributes
# create this for additional merge attributes. A better place for these would be the parent of this
{
params: params,
cart_id: params[:invoice],
status: params[:payment_status],
transaction_id: params[:txn_id],
first_name: params[:first_name],
last_name: params[:last_name],
email: params[:payer_email],
address_name: params[:address_name],
address_street: params[:address_street],
address_city: params[:address_city],
address_state: params[:address_state],
address_zip: params[:address_zip],
address_country: params[:address_country]
}
end
def extract_ipn_items_params
mod_params = Hash.new{|k, v| k[v] = {}.merge(additional_attributes) }
ITEM_PARAM_PREFIXES.each do |item_data_key|
key_tracker = 1
loop do
current_key = (item_data_key + key_tracker.to_s).to_sym
if params.include? current_key
mod_params[key_tracker][item_data_key] = params[current_key]
else
break
end
key_tracker += 1
end
end
mod_params
end
end
Let me know if that fixes your problem.
You should have payment_id so you can find it by using gem 'paypal-sdk-rest'
payment = PayPal::SDK::REST::Payment.find payment_id
then you could see all details in payment object

Model Error in CSV import - Ruby on Rails

I've been racking my brain for a while now and I can't figure out why my csv upload in my rails app is failing. I have a simple model that converts two names in the csv to integers of foreign_ids. The model works completely fine when executed manually in the console but for some reason it fails on the server. I get the error message: undefined method `id' for nil:NilClass
The model looks as follows:
require 'csv'
class Schedule < ActiveRecord::Base
belongs_to :team
belongs_to :opponent, :foreign_key => 'opponent_id', :class_name => 'Team'
def self.import(file)
CSV.foreach(file.path, headers: true, :header_converters => :symbol) do |row|
week_hash = row.to_hash
teamname = week_hash[:team]
teamhash = Team.where(:name => teamname).first
teamhash_id = teamhash.id
week_newhash = week_hash.reject!{ |k| k == :team}
week_newhash[:team_id] = teamhash_id
opponentname = week_hash[:opponent]
opponent_hash = Team.where(:name => opponentname).first
hashopponent_id = opponent_hash.id
week_newhash = week_newhash.reject!{ |k| k == :opponent}
week_newhash[:opponent_id] = hashopponent_id
Schedule.create!(week_newhash)
end
end
end
The problem must be in here somewhere. Any help would be greatly appreciated. Thanks.
I'm an idiot. The model was fine I just had a column mislabeled in my csv.
Maybe change:
teamhash_id = teamhash.id
to:
teamhash_id = teamhash[:id], and hashopponent_id = opponent_hash.id to hashopponent_id = opponent_hash[:id]?

Rails Netzke v0.10.1 - Refreshing grid panel within a tabpanel

I try to make a Netzke component with one master grid and subgrids in the south region of a Panel.
When a row in the maingrid is selected then should the subgrids be filtered with records related to the record in maingrid - like described here for an old netzke version:
https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/PFAQ-wYyNog/2RJgRLzh80oJ
I know that netzke is not further in development but I use it in a project.
ruby 2.1.2 (Mac OSX rbenv)
rails 4.0.10
netzke-core v0.10.1
netzke-basepack v0.10.1
Here my Code:
models:
class MbOrganisation < ActiveRecord::Base
has_many :mb_contacts
def customer_name
"#{orga_customer} - #{orga_name1}"
end
end
class MbContact < ActiveRecord::Base
belongs_to :mb_organisation
end
This is the central component
app/components/organisation_multitab.rb
class OrganisationMultitab < Netzke::Base
component :organisation_organisations
component :organisation_tabpanel do |c|
c.klass = MblixBaseTabpanel
c.items = [:organisation_contacts]
end
js_configure do |c|
c.layout = :border
c.border = false
c.init_component = <<-JS
function(){
// calling superclass's initComponent
this.callParent();
// setting the 'rowclick' event
var view = this.netzkeGetComponent('organisation_organisations').getView();
view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectItem({item_id: record.get('id')});
}, this);
}
JS
end
def configure(c)
super
c.items = [
{ items: [:organisation_organisations], region: :center },
{ items: [:organisation_tabpanel], region: :south, height: 200, split: true }
]
end
endpoint :select_item do |params, this|
# store selected id in the session for this component's instance
component_session[:selected_item_id] = params[:item_id]
end
end
These components are additionally used
Maingrid - organisation_organisations.rb
class OrganisationOrganisations < Netzke::Basepack::Grid
def configure(c)
super
c.model = "MbOrganisation"
c.columns = [:orga_customer, :orga_name1, :orga_name2, :orga_street, :orga_zip, :orga_city, :orga_tel, :orga_email]
c.force_fit = true
end
end
Component with Tabpanel- base_tabpanel.rb:
class BaseTabpanel < Netzke::Basepack::TabPanel
component :organisation_contacts do |c|
c.data_store = {auto_load: false}
c.scope = {:mb_organisation_id => component_session[:selected_item_id]}
c.strong_default_attrs = {:mb_organisation_id => component_session[:selected_item_id]}
end
def configure(c)
super
c.active_tab = 0
c.prevent_header = true
end
end
The grid component for the contacts:
class OrganisationContacts < Netzke::Basepack::Grid
def configure(c)
super
c.model = "MbContact"
c.columns = [{ :name => :mb_organisation__customer_name,
:header => "Organisation"
}, :cont_salutation, :cont_title, :cont_lastname, :cont_firstname, :cont_email, :cont_tel, :cont_mobile, :cont_birthday]
c.force_fit = true
end
end
The function this.selectItem(...) is correct triggered and calls the endpoint in OrganisationMultitab.
I have two problems/questions
First
- How can I automatically reload the stores of the subgrids in the tabpanel?
The described way in the linked google groups article: https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/PFAQ-wYyNog/2RJgRLzh80oJ is outdated (It's for netzke v0.5 - I use netzke v0.10.1):
{
:south => {
:item0 => {:load_store_data => aggregatee_instance(:south__item0).get_data},
:item1 => {:load_store_data => aggregatee_instance(:south__item1).get_data}
}
}
second problem: I got an error - when I manually refresh the subgrids:
ActiveModel::ForbiddenAttributesError in NetzkeController#direct
Update
The ActiveModel::ForbiddenAttributesError is solved by myself. There was a bug in the netzke-basepack gem:
Netzke::Basepack::Grid ran in an ActiveModel::ForbiddenAttributesError (rails 4 strong parameters) when the component, like above described, has a scope configured. (config[:scope] will later be merged to the params object that is an ActionController::Parameters object. - As the scope is database related this will be denied with ActiveModel::ForbiddenAttributesError )
My solution: In the endpoint.rb the ActionController::Parameters will be converted to a Hash - then the error is gone.
I made a fork and a pull request in github for this gem.
But
the second problem is not solved.
second problem: Now the subgrids can be manually refreshed without an error but they are always empty.
I guess the scope in the child component
component :organisation_contacts do |c|
c.data_store = {auto_load: false}
c.scope = {:mb_organisation_id => component_session[:selected_item_id]}
c. strong_default_attrs = {:mb_organisation_id => component_session[:selected_item_id]}
end
has no access to the value of the
component_session[:selected_item_id]
of the Organisation MultiTab parent component?
But it is neccessary to split the components - like described here: https://groups.google.com/forum/#!searchin/netzke/tabpanel/netzke/sDrU7NZIlqg/-2wGmed7fjcJ
Hope there is somebody who can help me. :-)
Thanks
Best regards
You're getting the ActiveModel::ForbiddenAttributesError because you're not permiting the attributes from the controller. Rails now uses strong_parameters instead of attr_accessible (like in Rails 3).
So I found the solution by my self.
First issue - reloading the grids in the Tabs
The store of the Ext gridcomponent can also be accessed in the Javascript.
So I extended the Javascript configuration of the OrganisationMulitab with this part:
Ext.each(this.netzkeGetComponent('organisation_tabpanel').items.items, function(item, index) {
item.getStore().load();
});
Second issue - send the selected id to the scope in the child component
The value must be sent to the session of the child component - so this does the job:
component_instance(:organisation_tabpanel).component_session[:selected_item_id] = params[:item_id]
instead of
component_session[:selected_item_id] = params[:item_id]
(The problem with the ActiveModel::ForbiddenAttributesError was a bug in the gem - solution is in my update of the question - I made a fork of the gem https://github.com/tomg65/netzke-basepack/tree/master-fixes-changes and sent a pull request to the original https://github.com/netzke/netzke-basepack/pull/158)
So the final code looks like this and all works fine:
class OrganisationMultitab < Netzke::Base
component :organisation_organisations
component :organisation_tabpanel do |c|
c.klass = MblixBaseTabpanel
c.items = [:organisation_contacts]
end
js_configure do |c|
c.layout = :border
c.border = false
c.init_component = <<-JS
function(){
// calling superclass's initComponent
this.callParent();
// setting the 'rowclick' event
var view = this.netzkeGetComponent('organisation_organisations').getView();
view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectItem({item_id: record.get('id')});
Ext.each(this.netzkeGetComponent('organisation_tabpanel').items.items, function(item, index) {
item.getStore().load();
});
}, this);
}
JS
end
def configure(c)
super
c.items = [
{ items: [:organisation_organisations], region: :center },
{ items: [:organisation_tabpanel], region: :south, height: 200, split: true }
]
end
endpoint :select_item do |params, this|
# store selected id in the session for child component's instance
component_instance(:organisation_tabpanel).component_session[:selected_item_id] = params[:item_id]
end
end
Hope this helps others too.
Best regards
Thomas

Active resource complaining about expects an hash

I am using active resource to get data from an api and display it,
My controller model.rb has
class Thr::Vol::Dom < ActiveResource::Base
class << self
def element_path(id, prefix_options = {}, query_options = nil)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}/#{id}#{query_string(query_options)}"
end
def collection_path(prefix_options = {}, query_options = nil)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}#{query_string(query_options)}"
end
end
ActiveResource::Base.site = 'http://10.00.0.00:8888/'
self.format = :json
self.collection_name= "/vv/test/domains"
def self.find
x = superclass.find(:one, :from => '/vv/test/domains/2013-06-25T05:03Z')
x
end
end
When i call this Thr::Vol::Dom.find method it returns the following error:
ArgumentError: expected an attributes Hash,
got ["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]
The api is expected to feed something like this
{"abs.com":["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]}
for the call i made.
The API returns the correct hash but i guess active resource is not able to read it properly, it is directly reading the value in the key-value pair of the hash.
I want to fix this "ArgumentError" error , i want to display the contents of the returned hash in the view.
You can change how ActiveResource handle json response with
class YourModel < ActiveResource::Base
self.format = ::JsonFormatter.new(:collection_name)
end
In lib/json_formatter.rb
class JsonFormatter
include ActiveResource::Formats::JsonFormat
attr_reader :collection_name
def initialize(collection_name)
#collection_name = collection_name.to_s
end
def decode(json)
remove_root(ActiveSupport::JSON.decode(json))
end
private
def remove_root(data)
if data.is_a?(Hash) && data[collection_name]
data[collection_name]
else
data
end
end
end
If you pass self.format = ::JsonFormatter.new(:categories) it will find and remove categories root element in your json returned by your API.
The API is returning a JSON object, not a Ruby hash. You'll need to convert it into a hash by using Ruby's JSON module:
require 'JSON'
hash = JSON.parse('{"abs.com":["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]}')
This will return a hash and then you'll notice that the key/value pair will work as expected:
hash["abs.com"] => ["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]

Ruby soap4r build headers

Again with the soap.
I am trying to build a header using soap4r that is supposed to look like this
<SOAP-ENV:Header>
<ns1:UserAuthentication
SOAP-ENV:mustUnderstand="1"
SOAP-ENV:actor="http://api.affiliatewindow.com">
<ns1:iId>*****</ns1:iId>
<ns1:sPassword>*****</ns1:sPassword>
<ns1:sType>affiliate</ ns1:sType>
</ns1:UserAuthentication>
<ns1:getQuota SOAP-ENV:mustUnderstand="1" SOAP-
ENV:actor="http://api.affiliatewindow.com">true</ns1:getQuota>
</SOAP-ENV:Header>
What I have done is created a header derv. class
AffHeader < SOAP::Header::SimpleHandler
Created a UserAuthentification element.
def initialize
#element = XSD::QName.new(nil, "UserAuthentification")
super(#element)
end
And return a hash
def on_simple_outbound
self.mustunderstand = 1
{ "iId" => ID, "sPassword" => PASSWORD, "sType" => "affiliate" }
end
How can I make my header look like I want further. How do I add the actor for example.
I am going to keep searching on this, any Help is very appreciated.
Thank you
In SOAP4R, the target_actor attribute is read only but you can add a new method like:
def target_actor= (uri)
#target_actor = uri
end
and in your on_simple_outbound method, you can call target_actor with your uri like so:
def on_simple_outbound
self.mustunderstand = 1
self.target_actor = "http://api.affiliatewindow.com"
{ "iId" => ID, "sPassword" => PASSWORD, "sType" => "affiliate" }
end
E.g.
irb(main):003:0> h = AffHeader.new
=> #<AffHeader:0x3409ef0 #target_actor=nil, #encodingstyle=nil,
#element=#<XSD::QName:0x1a04f5a {}UserAuthentification>,
#mustunderstand=false, #elename=#<XSD::QName:0x1a04f5a {}UserAuthentification>>
irb(main):006:0> h.on_simple_outbound
=> {"sType"=>"affiliate", "sPassword"=>"secret", "iId"=>"johndoe"}
irb(main):007:0> h
=> #<AffHeader:0x3409ef0 #target_actor="http://api.affiliatewindow.com",
#encodingstyle=nil,
#element=#<XSD::QName:0x1a04f5a {}UserAuthentification>,
#mustunderstand=1, #elename=#<XSD::QName:0x1a04f5a
{}UserAuthentification>>

Resources