Passing post data to other pages in Sinatra - ruby

This seems to work fine:
views/index.haml:
%form{:method => 'POST' :action => '/'}
%label{:for => 'name'} Name:
%input{:type => 'text, :value => #values[:name] || ""}
%input{:type => 'submit'}
app.rb:
post '/' do
#values = params
haml :review
end
views/review.rb
Hello #{params[:name]}!
However, when I try to send my post-data to the same view on a different URL I get an error, or in other words:
app.rb:
post '/' do
#values = params
redirect '/review'
end
get '/review' do
#values = params
haml :review
end
The data is not going through, but no error is raised.
How do I send the post-data across pages like this? Ideally, I do not want to create a database.

You can store the parameters in a session or specify the query string explicitly. Browser Redirect from Sinatra Documentation
As specified in the documentation, you may use sessions or convert the POST params to a query string and use it in the redirect method. A crude example would be:
Say the POST params hash inside the '/' block is:
{
:name => "Whatever",
:address => "Wherever"
}
This hash can be made into a string like so:
query = params.map{|key, value| "#{key}=#{value}"}.join("&")
# The "query" string now is: "name=Whatever&address=Wherever"
Now use this inside the post '/' do
redirect to("/review?#{query}")

Related

Testing with Postman keeps returning Invalid JSON error

I have created REST Api in Ruby on Sinatra platform. I am testing the service with Postman and whatever JSON form I try to POST I keep getting an error 400 Invalid JSON SUCKER. The error is defined on the back end in case of invalid JSON form. Please take a look at the back end and tell me what am I doing wrong.
I have to mention that GET method works with Postman and cURL from the command line while POST works only if I use it with cURL but NOT in POSTMAN.
#server.rb
require 'sinatra'
require 'mongoid'
require 'sinatra/namespace'
require 'sinatra/base'
#require 'json'
before do
content_type :json
headers 'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST', 'PATCH']
end
#load database config
Mongoid.load! "mongoid.config"
#Class Company
class Company
include Mongoid::Document
field :compid, type: String
field :name, type: String
field :address, type: String
field :city, type: String
# validates :compid, presence: true
# validates :name, presence: true
index({ compid:1 }, { unique: true, name: "compid_index" })
index({ name: 'text' })
scope :name, -> (name) { where(name: /^#{name}/) } # this could be changed to /^#{title}/i to make case insensitive searcg =end
scope :compid, -> (compid) { where(compid: compid) }
end
#Serializers
class Serializer
def initialize(company)
#company = company
end
def as_json(*)
data ={
id:#company.compid.to_s,
name:#company.name,
address:#company.address,
city:#company.city,
}
data[:errors] = #company.errors if#company.errors.any?
data
end
end
# Endpoints
get '/' do
'List of all Companies'
end
namespace '/api/v1' do
before do
content_type 'application/json'
end
helpers do
def base_url
#base_url ||= "#{request.env['rack.url_scheme']}://{request.env['HTTP_HOST']}"
end
def json_params
begin
JSON.parse(request.body.read)
rescue
halt 400, { message:'Invalid JSON' }.to_json
end
end
end
get '/companies' do
companies = Company.all
[ :name, :compid,].each do |filter|
companies = companies.send(filter, params[filter]) if params[filter]
end
#put it through the serializer not to get all te atributes
companies.map { |company| Serializer.new(company) }.to_json
end
get '/companies/:compid' do |compid| #get the details about the company by searching with compid
company = Company.where(compid: compid).first
halt(404, { message:'Company Not Found'}.to_json) unless company
Serializer.new(company).to_json
end
post '/companies' do
company = Company.new(json_params)
if company.save
response.headers['Location'] = "#{base_url}/api/v1/companies{company.copmid}" # "{company.id}"
status 201
else
status 422
body Serializer.new(company).to_json
end
end
The data that I GET with the Postman looks like this:
[{"id":"5a1271f7943e8a0f5fd76008","name":"The Power Of Habit","address":"Charles Duhigg Vej","city":"Viborg"}]
I have tried to POST data in various forms:
[{"id":"5a1271f79asdd76008","name":"The Power Of Habit","address":"Charles Duhigg Vej","city":"Viborg"}]
{"id":"5a1271f79asdd76008","name":"The Power Of Habit","address":"Charles Duhigg Vej","city":"Viborg"}
[{"compid":"5a1271f79asdd76008","name":"The Power Of Habit","address":"Charles Duhigg Vej","city":"Viborg"}]
{"compid":"5a1271f79asdd76008","name":"The Power Of Habit","address":"Charles Duhigg Vej","city":"Viborg"}

How to get json response in rails api

I want to display all programmes which I got from a query as json response. I'm getting the programmes, but don't know how to render them through json. I'm using the jbuilder gem and created a create.json.buider.rb file. In my query I'm getting everything correctly, but I'm not receiving a JSON response with whatever details in I have in the query.
This is my controller. I have tried it like this but I'm not getting a json response. Only a status as 200.
class Api::V1::Categories::ProgrammesController < ApiController
respond_to :json
def category
#category=Category.all
#programmes=Programme.joins(:category).find_by(category_id: params[:category_id])
if #programmes.present?
render :json=> {:message=>"Programme not exists "}, :status=>422
else
render :json => #programmes
end
end
end
My create.json.jbuilder file:
json.programmes #programmes
I think you should change #programmes to { :programmers => #programmes.as_json }
class Api::V1::Categories::ProgrammesController < ApiController
def category
#category = Category.all
#programmes = Programme.joins(:category).find_by(category_id: params[:category_id])
if #programmes.present?
render :json=> {:message=>"Programme not exists "}, :status=>422
else
render :json => { :programmers => #programmes.as_json }
end
end
end

Why can't i get sendgrid to replace my variables in my template when using their smtpapi?

I believe that i have followed the documentation on SendGrid's site but so far when I receive an email that I've sent through their API it never substitutes the replacements i've specified in the x-smtpapi headers. I am using HTTParty to send the request like this:
HTTParty.post(Sendgrid::Postman.api_url, {
:query => params.merge({ "api_user" => #config[:api_user], "api_key" => #config[:api_key] }),
:headers => headers, :format => :json
})
the "params" look like this:
{"from"=>"noreply#foo.com", "text"=>"Happy Holidays -first_name- -last_name-,\nI hope this message finds you in good health and high spirits.", "to"=>["foo#gmail.com"], "subject"=>"foo"}
The Headers look like this:
{"X-SMTPAPI"=>"{\"sub\": {\"-first_name-\": [\"Foo\"], \"-email-\": [\"foo#gmail.com\"], \"-login-\": [\"heavysixer\"], \"-last_name-\": [\"Bar\"]}, \"to\": [\"foo#gmail.com\"]}"}
The mail is always successfully delivered but when it arrives in the inbox the values that were supposed to be substituted still appear like -first_name- & -last_name-
What am I missing? I've been messing around with this for a solid day now?
-----------------------------------------------------------
UPDATE:
Per the advice below I have tried to push the x-smtpapi params into the form post yet I am getting the same result. The query string for my post now looks like this:
params = {"api_user" => 'foo', "api_key" => 'bar', "from"=>"noreply#foo.com", "text"=>"Happy Holidays -first_name- -last_name-,\nI hope this message finds you in good health and high spirits.", "to"=>["foo#gmail.com"], "subject"=>"foo", "x-smtpapi"=>{"sub"=>{"-first_name-"=>["foo"], "-email-"=>["foo#gmail.com"], "-login-"=>["foo"], "-last_name-"=>["bar"]}}}
HTTParty.post(Sendgrid::Postman.api_url, :query => params, :format => :json)
Their documentation implies that x-smtpapi should be one of the posted parameters, not an http header.

HTTParty double escapes my json

I have a few models in Ruby that I need to send to a Java/RestEasy server via HTTParty/Put.
configuration_mapping.rb:
def as_json(options = {})
{:configGroup => #group, :coordinates => {#key => #value}}
end
def self.put(endpoint, content, tier = 'nursery')
response = HTTParty.put(base_uri + endpoint, json_payload(content))
end
def self.json_payload(content)
{
:body => content.to_json,
:format => :json,
:headers => {"Content-Type" => "application/json", "content-type" => "application/json", "Accept" => "application/json"}
}
end
The JSON gets double escaped:
{ :body=>" {
\"configGroup\":\"test\",
\"coordinates\":{
\"Integration Test Key\":\"moo\"
} } ", :format=>:json, :headers=>{" Content-Type"=>"application/json", "content-type"
=>"application/json", "Accept" =>"application/json" } }
And Jackson JSON parser borks:
2011-11-27 15:34:11,179 ERROR [tp-1442358158-0] [REPORT] []
[asy.core.SynchronousDispatcher] Failed executing PUT
/v1/groups/test/mappings;tester=Integration
Test;tier=qa;timeStamp=-4712-01-01
org.jboss.resteasy.spi.ReaderException:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize
instance of java.lang.String out of START_OBJECT token at [Source:
org.mortbay.jetty.HttpParser$Input#4092fef5; line: 1, column: 22] at
I tried letting httparty convert to json for me, thinking httparty escaped the characters, writing my own as_json method, but this is the output which is not the json I wanted, the wrong fields are in here and my as_json method is not called:
{:body=>Config::Client::ConfigurationMapping:0x00000100c78930
#dimensions={"tester"=>"Integration Test", "tier"=>"qa",
"timeStamp"=>"-4712-01-01"}, #key="Integration Test Key",
#group="test", #value="moo">, :format=>:json,
:headers=>{"Content-Type"=>"application/json",
"content-type"=>"application/json", "Accept"=>"application/json"}}
What is causing the string to become double escaped?
I figured out the problem, my json wasn't formatted correctly. I reformatted the packet and it worked.

How to mock an instance method of an already mocked object?

I need to mock the following:
Class User
def facebook
#returns an instance of a facebook gem
end
end
So in my User tests, to access the User's facebook info I need to call user.facebook.me.info to retrieve its info. If I want to mock this, I'm currently using the following:
#user = Factory(:user)
facebook = mock()
me = mock()
me.expects(:info).returns({"name" => "John Doe"})
facebook.expects(:me).returns(me)
#user.expects(:facebook).returns(facebook)
assert_equal "John Doe", #user.facebook.me.info["name"]
This works but seems a bit unwieldy, is there a better way to do this ?
[edit] I'm using mocha as mocking framework
You could try something like this :-
user = Factory(:user)
user.stubs(:facebook => stub(:me => stub(:info => {:name => "John Doe"})))
If you really want to check that all these methods are called (which I suspect you don't), you could do the following :-
user = Factory(:user)
user.expects(:facebook => mock(:me => mock(:info => {:name => "John Doe"})))
It's a bit more verbose, but it's usually worthwhile giving each mock object a name :-
user = Factory(:user)
user.stubs(:facebook => stub('facebook', :me => stub('me', :info => {:name => "John Doe"})))
I hope that helps.
If you don't want to check that all the methods are called, you can also use different alternatives to mocking. For instance, you can use an OpenStruct.
#user = Factory(:user)
facebook = mock()
me = mock()
me.expects(:info).returns({"name" => "John Doe"})
facebook.expects(:me).returns(me)
#user.expects(:facebook).returns(facebook)
assert_equal "John Doe", #user.facebook.me.info["name"]
becomes
#user = Factory(:user)
#facebook = OpenStruct.new(:me => OpenStruct.new(:info => {:name => "John Doe"}))
#user.expects(:facebook).returns(#facebook)
This solution also offers you the advantage that you can change the #facebook properties in your tests, if you need to test different conditions.

Resources