Rails - WebSocket - ruby-on-rails

I want to push data to client using WebSocket. I dont now how to return data(from Redis) to client. Could somebody explain me that?
sockets_controller.rb
class SocketsController < WebsocketRails::BaseController
def create
send_message :new_event, {:message => 'TEST'}
end
end
events.rb
WebsocketRails::EventMap.describe do
subscribe :new_event, 'sockets#create'
end
websocket_rails.rb
WebsocketRails.setup do |config|
config.redis_options = { driver: :ruby }
config.standalone = false
config.synchronize = true
end
Client side:
$socket.on('test', function(data) {
console.log(data);
$scope.events.upshift(data);
if($scope.events.length>50){
$scope.events.length=50;
EventsList =$scope.events;
}
});
routerApp.config(["$socketProvider", function ($socketProvider) {
$socketProvider.setUrl("http://172.16.20.179:3000/events");
}]);
On the client side issue looks like:
GET http://172.16.20.179:3000/socket.io/?
EIO=3&transport=polling&t=1439215071732-9
XMLHttpRequest cannot load http://172.16.20.179:3000/socket.io/?
EIO=3&transport=polling&t=1439215056291-6. No 'Access-Control-Allow-
Origin' header is present on the requested resource. Origin
'http://localhost:4000' is therefore not allowed access. The response
had HTTP status code 404.

It seems that you are trying to use Socket.IO for web-sockets. There are no supported implementation of Socket.IO for Rails.
If you want an example on how to interact with websocket-rails from Angular.JS, you can use, for example, the following guide:
https://medium.com/#karimbutt/integrating-websocket-rails-with-angularjs-or-other-js-frameworks-256729e95a86

Related

How to Consume SOAP with Savon in Rails app

I need to communicate to a service called ifthenpay via Soap using Savon on a Rails app that i'm working on.
The service generates payment references so users could pay on home banking or in cash machines.
The app needs to communicate to the service to see if the payment was made or not.
I'm using Savon and this is what i have so far in the checkout model(don't know if this is the right place to put the above code):
def self.check_status!
client = Savon.client(
wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL",
endpoint: "http://localhost:3000",
namespaces: {"xmlns:ift"=>"https://www.ifthensoftware.com/"}
)
begin
response = client.call(:get_payments, message: check_status_hash)
rescue Savon::SOAPFault => error
#...
end
end
def self.check_status_hash
{
"ift:get_payments" => {
"ift:chavebackoffice" => { "ift:chavebackoffice" => "0000-0000-0000-0000" },
"ift:entidade" => {"ift:entidade" => "11202"},
"ift:subidentidade" => {"ift:subidentidade" => "202"},
"ift:dtHrInicio" => {"ift:dtHrInicio" => ""},
"ift:dtHrFim" => {"ift:dtHrFim" => ""},
"ift:referencia" => {"ift:referencia" => ""},
"ift:valor" => {"ift:valor" => ""}
}
}
end
I've an admin page where i need to list all the payments that have been made, so i can manage what was selled.
You can see the service operations here
What do i need to put in the controller and in the view for this to work?
I really appreciate your help, because i'm struggling with this for a long time.
From my point of view, and pardon me because I'm not very experienced with the use of savon, you are slightly overkilling this.
To start with, you are providing the client with a WSDL url, so what is the use of attaching a doubtfully necessary endpoint?
A namespace is, to my understanding, necessary, once again, in case there is no standard WSDl interface.
I would go, to start off, I would simply go for:
#client = Savon.client(wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL")
Watch the #client instead of client. We need to assign the client to a variable that will be reachable throughout the entire process (request, process, response).
Next, you will need to prepare your request. Parsing the above url, there is a banch of methods. You are providing in your example getPayments request.
I will not use this space to tell you how to construct the hash, but the hash should look something like this:
request_hash = {
chavebackoffice: "0000-0000-0000-0000",
entidade: "11202",
subidentidade: "202",
dtHrInicio: "",
dtHrFim: "",
referencia: "",
valor: ""
}
To make the call to the api, you should simply do this:
#response = #client.call(:get_payments) do
message request_hash
end
And then, parse the #response. You will probably need to turn it to a hash first. Maybe something like this:
#data = #response.to_hash[:get_payments_response][:get_payments_result][:ifmb]
I hope this will help you enough. It should be more than enough.
Putting all up: Controller code, adapt to your need
before_action :set_client, only: [:get_payments, :other_actions_perhaps]
def get_payments
# params[:whatever] in case you post to #whatever object
# params without [:whatever] if you are using "GET" method
request_hash = {
chavebackoffice: params[:whatever][:chavebackoffice],
entidade: params[:whatever][:entidade],
subidentidade: params[:whatever][:subidentidade],
dtHrInicio: params[:whatever][:dtHrInicio],
dtHrFim: params[:whatever][:dtHrFim],
referencia: params[:whatever][:referencia],
valor: params[:whatever][:valor]
}
response = #client.call(:get_payments) do
message request_hash
end
# use this #data in your view
#data = response.to_hash[:get_payments_response][:get_payments_result][:ifmb]
end
def set_client
#client = Savon.client(wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL")
end

what is Instagram API basic access in sandbox?

I try to use instagram API in sandbox.
I use Ruby with gem 'instagram'
But I can only call method
Instagram.user_recent_media( {:count => 5})
def user_recent_media(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
id = args.first || "self"
response = get("users/#{id}/media/recent", options)
response
end
like #instagram = Instagram.user_recent_media( {:count => 5})
I try to call other method like
def follow_user(id, options={})
options["action"] = "follow"
response = post("users/#{id}/relationship", options, signature=true)
response
end
get
Instagram::BadRequest in HomeController#index
GET
https://api.instagram.com/v1/users/[user_ID]/follows.json?access_token=[Access_token]&max_id=10:
400: This request requires scope=follower_list, but this access token
is not authorized with this scope. The user must re-authorize your
application with scope=follower_list to be granted this permissions.
Is there any method I can use ??
instagram-ruby-gem API
Basically, you have to register your application with Instagram here and hope they accept it. There isn't a way around this.
Here's a little reading on how they basically said "buzz off" to third party app developers http://www.businessinsider.com/instagram-made-a-change-that-stopped-lots-of-third-party-apps-from-working-2016-6

mixing redis actioncontroller::live - rails app

I am using for the first time redis to put chat functionality in my rails app, following this
I have in my javascript`
$(document).ready ->
source = new EventSource('/messages/events')
source.addEventListener 'messages.create', (e) ->
message = $.parseJSON(e.data).message
console.log(message)
$(".chat-messages").append "#some code"
and in my message controller
def create
response.headers["Content-Type"] = "text/javascript"
attributes = params.require(:message).permit(:content, :sender_id, :sendee_id)
#message = Message.create(attributes)
respond_to do |format|
format.js { render 'messages/create.js.erb' }
end
$redis.publish('messages.create', #message.to_json)
end
def events
response.headers["Content-Type"] = "text/event-stream"
redis = Redis.new
redis.subscribe('messages.*') do |on|
on.message do |pattern, event, data|
response.stream.write("event: #{event}\n")
response.stream.write("data: #{data}\n\n")
end
end
rescue IOError
logger.info "Stream closed"
ensure
redis.quit
response.stream.close
end
The problem is that first, nothing is logged in my console, and second I get numbers of random ConnectionTimeoutError errors. Some one hava an idea what's going on
Pre-Reqs:
Ruby 2.0.0+
Rails 4.0.0+
Redis
Puma
Initializer:
Create a redis.rb initializer file in the config/initializers directory, globalizing an instance of redis. It's also a good idea to set up a heartbeat thread (Anything from 5 seconds to 5 minutes is okay, depending on your requirements):
$redis = Redis.new
heartbeat_thread = Thread.new do
while true
$redis.publish("heartbeat","thump")
sleep 15.seconds
end
end
at_exit do
# not sure this is needed, but just in case
heartbeat_thread.kill
$redis.quit
end
Controller:
You need to add two methods to your ChatController, pub and sub. The role of pub is to publish chat events and messages to redis, and sub to subscribe to these events. It should look something like this:
class ChatController < ApplicationController
include ActionController::Live
skip_before_filter :verify_authenticity_token
def index
end
def pub
$redis.publish 'chat_event', params[:chat_data].to_json
render json: {}, status: 200
end
def sub
response.headers["Content-Type"] = "text/event-stream"
redis = Redis.new
redis.subscribe(['chat_event']) do |on|
on.message do |event, data|
response.stream.write "event: #{event}\ndata: #{data}\n\n"
end
end
rescue IOError
logger.info "Stream Closed"
ensure
redis.quit
response.stream.close
end
end
In your routes, make pub a POST and sub a GET, and match the path to something like /chat/publish and /chat/subscribe.
Coffeescript / Javascript:
Assuming your actual webpage for the chat app is at /chat, you need to write some Javascript to actually send and receive chat messages.
For ease of understanding, let's suppose your webpage only has a textbox and a button. Hitting the button should publish the content of the textbox to the chat stream, we can do that using AJAX:
$('button#send').click (e) ->
e.preventDefault()
$.ajax '/chat/publish',
type: 'POST'
data: {
chat_data: {
message: $("input#message").val(),
timestamp: $.now()
}
}
error: (jqXHR, textStatus, errorThrown) ->
console.log "Failed: " + textStatus
success: (data, textStatus, jqXHR) ->
console.log "Success: " + textStatus
Now, you need to be able to subscribe and receive the chat messages as well. You need to use EventSource for this. Using EventSource, open a channel for SSE so that you can receive events, and use that data to update the view. In this example, we will only log them to the javascript console.
The code should look something like this:
$(document).ready ->
source = new EventSource('/chat/subscribe')
source.addEventListener 'chat_event', (e) ->
console.log(e.data)
Enable Parallel Requests:
In your development environment, you'll have to enable parallel requests by adding these two lines to your config/environments/development.rb:
config.preload_frameworks = true
config.allow_concurrency = true
Now fire up your browser, browse to /chat and see the magic. When you type a message and click the button, the message will be received by all instances of that webpage.
Well this is how you make a basic chat application in rails using ActionController::Live and Redis. The final code would obviously be very different depending on your requirements but this should get you started.
Some more resources you should check out:
Tender Love Making - Is it Live?
Railscasts - #401 - ActionController::Live
SitePoint - Mini Chat with Rails and SSEs
Github - mohanraj-ramanujam / live-stream
Thoughtbot - Chat Example using SSEs
Although I've not used redis in this capacity (a mediator for "live" data), I managed to get this functionality working with Pusher
Redis
I don't understand how you're keeping the connection open between your app & Redis. You'll need some sort of web socket or concurrent-connection tech in place to handle the updates -- and to my knowledge, Redis does not handle this directly
If you look at this example, it uses a server called Goliath to handle the asynchronous connectivity:
When tiny-chat connects to the server it sends a GET request to
/subscribe/everyone where everyone is the name of the channel and with
the “Accept” header set to text/event-stream. The streaming middleware
(above) receives this request and subscribes to a redis Pub/Sub
channel. Since Goliath is non-blocking multiple clients can be
listening for events without tying up a Heroku dyno. The payload of a
server sent event looks like this:
That basically uses Middleware to connect you to the redis server -- allowing you to receive updates as required
Code
Although I can't pinpoint any errors specifically, I can give you some code we're using (using Pusher):
#config/initializers/pusher.rb
Pusher.url = ENV["PUSHER_URL"]
Pusher.app_id = ENV["PUSHER_APP_ID"]
Pusher.key = ENV["PUSHER_KEY"]
Pusher.secret = ENV["PUSHER_SECRET"]
#app/controllers/messages_controller.rb
def send_message
id = params[:id]
message = Message.find(id).broadcast!
public_key = self.user.public_key
Pusher['private-user-' + public_key].trigger('message_sent', {
message: "Message Sent"
})
end
#app/views/layouts/application.html.erb
<%= javascript_include_tag "http://js.pusher.com/2.1/pusher.min.js" %>
#app/assets/javascripts/application.js
$(document).ready(function(){
#Pusher
pusher = new Pusher("************",
cluster: 'eu'
)
channel = pusher.subscribe("private-user-#{gon.user}")
channel.bind "multi_destroy", (data) ->
alert data.message
channel.bind "message_sent", (data) ->
alert data.message
});

Is there a way to set the default namespace with Savon::Model?

Savon is stubborn in generating SOAP envelopes from WSDL's. It does it improperly and I see no way to fix it. It also takes the liberty of inserting the wsdl: namespace on everything for whatever reason.
The request I am building uses the tns: namespace. I'd love to be able to use Savon::Model, but right now I have to do:
client.request :tns, :function_name do
soap.body = { params }
end
Instead of something like:
super(params)
Making the request block in every function is tedious, and I have to define the function name every time instead of Savon automatically calling the correct function like what would happen in the ideal case. Right now my functions are looking like
def foo
client.request :tns, :foo do
...
end
Having to say "foo" twice seems ridiculous. Is there a way to set the default namespace for every request in a class that extends Savon::Model?
client = Savon.client do
wsdl "blah blah"
element_form_default :qualified
namespace_identifier :tem
env_namespace :soapenv
end
I am not sure if I understand your questions. I assume you are asking how to set the default namespace and wrap the request body in a function, so you don't need to write the request body every time. This code works for me, but I removed some irrelevant parts
class ExampleWS
EXAMPLE_WS_DEFAULT_NAMESPACE = "urn:example:request:1.0.0"
......
def getStockPrice( locale, stockId )
response = $client.request :get_stock_price do
soap.input = [
"ns1:getStockPrice",
{
"xmlns:ns1" => EXAMPLE_WS_DEFAULT_NAMESPACE #set default namespace here
}
]
soap.body = {
"locale" => locale,
"stockId" => stockId
}
end
end
......
end
......
# call the function
getStockPrice("en_US", 123 )
This works for me. It uses Savon 2, though:
class Soapservice
extend Savon::Model
client wsdl: "http://example.com?wsdl", env_namespace: :tns,
operations :get_resource, :put_resource
def :get_resource(id)
super(message: { id: id })
end
end
service = Soapservice.new
response = service.get_resource(1) #overwriting get_resource
# or
response = service.put_resource(message: { username: "luke", secret: "secret" })
(My example builds on the one from the official savon homepage)

Problems with MailChimp API in Ruby Error Code: -90

I am using the following code in my MailChimp Controller to submit simple newsletter data. When It is sent I receive the following error as a "Method is not exported by this server -90" I have attached my controller code below. I am using this controller for a simple newsletter signup form. (Name, Email)
class MailchimpController < ApplicationController
require "net/http"
require "uri"
def subscribe
if request.post?
mailchimp = {}
mailchimp['apikey'] = 'f72328d1de9cc76092casdfsd425e467b6641-us2'
mailchimp['id'] = '8037342dd1874'
mailchimp['email_address'] = "email#gmail.com"
mailchimp['merge_vars[FNAME]'] = "FirstName"
mailchimp['output'] = 'json'
uri = URI.parse("http://us2.api.mailchimp.com/1.3/?method=listSubscribe")
response = Net::HTTP.post_form(uri, mailchimp)
mailchimp = ActiveSupport::JSON.decode(response.body)
if mailchimp['error']
render :text => mailchimp['error'] + "code:" + mailchimp['code'].to_s
elsif mailchimp == 'true'
render :text => 'ok'
else
render :text => 'error'
end
end
end
end
I highly recommend the Hominid gem: https://github.com/tatemae-consultancy/hominid
The problem is that Net::HTTP.post_form is not passing the "method" GET parameter. Not being a big ruby user, I'm not certain what the actual proper way to do that with Net::HTTP is, but this works:
require "net/http"
data="apikey=blahblahblah"
response = nil
Net::HTTP.start('us2.api.mailchimp.com', 80) {|http|
response = http.post('/1.3/?method=lists', data)
}
p response.body
That's the lists() method (for simplicity) and you'd have to build up (and urlencode your values!) your the full POST params rather than simply providing the hash.
Did you take a look at the many gems already available for ruby?
http://apidocs.mailchimp.com/downloads/#ruby
The bigger problem, and main reason I'm replying to this, is that your API Key is not obfuscated nearly well enough. Granted I'm used to working with them, but I was able to guess it very quickly. I would suggest immediately going and disabling that key in your account and then editing the post to actually have completely bogus data rather than anything close to the correct key. The list id on the other hand, doesn't matter at all.
You'll be able to use your hash if you convert it to json before passing it to Net::HTTP. The combined code would look something like:
mailchimp = {}
mailchimp['apikey'] = 'APIKEYAPIKEYAPIKEYAPIKEY'
mailchimp['id'] = '8037342dd1874'
mailchimp['email_address'] = "email#gmail.com"
mailchimp['merge_vars[FNAME]'] = "FirstName"
mailchimp['output'] = 'json'
response = nil
Net::HTTP.start('us2.api.mailchimp.com', 80) {|http|
response = http.post('/1.3/?method=listSubscribe', mailchimp.to_json)
}

Resources