POSTing File Attachments over HTTP via JSON API - ruby-on-rails

I have a model called Book, which has_many :photos (file attachments handled by paperclip).
I'm currently building a client which will communicate with my Rails app through JSON, using Paul Dix's Typhoeus gem, which uses libcurl.
POSTing a new Book object was easy enough. To create a new book record with the title "Hello There" I could do something as simple as this:
require 'rubygems'
require 'json'
require 'typhoeus'
class Remote
include Typhoeus
end
p Remote.post("http://localhost:3000/books.json",
{ :params =>
{ :book => { :title => "Hello There" }}})
My problems begin when I attempt to add the photos to this query. Simply POSTing the file attachments through the HTML form creates a query like this:
Parameters: {"commit"=>"Submit", "action"=>"create", "controller"=>"books", "book"=>{"title"=>"Hello There", "photo_attributes"=>[{"image"=>#<File:/var/folders/1V/1V8Kw+LEHUCKonqJ-dp3oE+++TI/-Tmp-/RackMultipart20090917-3026-i6d6b9-0>}]}}
And so my assumption is I'm looking to recreate the same query in the Remote.post call.
I'm thinking that I'm letting the syntax of the array of hashes within a hash get the best of me. I've been attempting to do variations of what I was expecting would work, which would be something like:
p Remote.post("http://localhost:3000/books.json",
{ :params =>
{ :book => { :title => "Hello There",
:photo_attributes => [{ :image => "/path/to/image/here" }] }}})
But this seems to concatenate into a string what I'm trying to make into a hash, and returns (no matter what I do in the :image => "" hash):
NoMethodError (undefined method `stringify_keys!' for "image/path/to/image/here":String):
But I also don't want to waste too much time figuring out what is wrong with my syntax here if this isn't going to work anyway, so I figured I'd come here.
My question is:
Am I on the right track? If I clear up this syntax to post an array of hashes instead of an oddly concatenated string, should that be enough to pass the images into the Book object?
Or am I approaching this wrong?

Actually, you can't post files over xhr, there a security precaution in javascript that prevents it from handling any files at all. The trick to get around this is to post the file to a hidden iframe, and the iframe does a regular post to the server, avoiding the full page refresh. The technique is detailed in several places, possibly try this one (they are using php, but the principle remains the same, and there is a lengthy discussion which is helpful):
Posting files to a hidden iframe

Related

asset_path (with fingerprint) from database?

I develop a rails app with exercises (for kids with learning difficulties in math). The interactive part of the exercises is written in javascript. I store each exercise in a database. The javascript contains
<%= asset_path('to_images') %>
I can read the scripts into the controller and write the content to a partial, but I think it would be better to capture the scripts in a variable, like:
#animation = exercise.animation
where any code containing <%= asset_path(...) %> would be replaced with the correct fingerprinted route to the asset.
Here is an example of a code snippet in exercise.animation:
$("#hundred_square td").css({
backgroundImage: 'url(<%= asset_path("exercises/shapes/circles/circle_open_black_48.png") %>)',
backgroundSize: "2vw",
backgroundRepeat: "no-repeat",
backgroundPosition: "center"
});
I have already tried to
class Exercise < ActiveRecord::Base
include ActionView::Helpers::AssetUrlHelper
and
self.animation.gsub(/\<\%\=\s*asset_path\((.+)\)\s*\%\>/) do |match|
address = $1
puts "#{address}"
=> "exercises/shapes/circles/circle_open_black_48.png"
puts "#{asset_path(address)}"
=> /"exercises/shapes/circles/circle_open_black_48.png"
puts "#{ActionController::Base.helpers.asset_path(address)}"
=> /"exercises/shapes/circles/circle_open_black_48.png"
end
do not produce the result I need.
Thanks for your suggestions!
One way to fetch the MD5 fingerprint value is to use Sprockets' find_asset method, passing in a logical path to your asset to get a Sprockets::BundledAsset instance. For example
[1] pry(main)> Rails.application.assets.find_asset('application.js')
=> #<Sprockets::BundledAsset:0x3fe368ab8070 pathname="/Users/deefour/Sites/MyApp/app/assets/javascripts/application.js", mtime=2013-02-03 15:33:57 -0500, digest="ab07585c8c7b5329878b1c51ed68831e">
You can call digest_path on this object to get it's MD5 sum appended to the asset.
[1] pry(main)> Rails.application.assets.find_asset('application.js').digest_path
=> "application-ab07585c8c7b5329878b1c51ed68831e.js"
With this knowledge you can create a helper to return the digest_path for any asset in your application, call this helper from within your .js.erb files or from within your model.
See this answer for more details on this approach.

Type Error in RSpec controller spec

I am having troubles with an RSPec test. The test does a PUT with some objects in the request. The controller which receives the PUT seems to be not getting the correct values
For example, 'put :update, :id => #channel.id, :channel => #channel, :tags => #tag' Then, in the Controller, when I try to use params[:tags] there is an integer in that location. A Gist with the Spec and the controller method is at https://gist.github.com/3715021
This started happening when I upgraded from Rails 3.0.13 to 3.1.8
Any idea what might be happening here and how to resolve it?
I'm assuming that #tag is an object from your Tags model. When you give Rails an object like
`get :action, :foo => foo`
or in a url helper (e.g., foo_path(foo)),
Rails will turn your object into a parameter suitable for use in a url via the #to_param method. You're probably getting an integer because Tag#to_param returns the id of the tag in your db.
It looks like your update action, by contrast, expects params[:tags] to be a hash, presumably generated from a form that includes fields for values like tags[:name].
I can't help much more without knowing more about the relevant code. But I'm guessing what you want to do is change your test to read
put :update, :id => #channel.id, :channel => #channel, :tags => { :name => 'tag' }
or something like that, mimicking the params you'd get by actually submitting the form that PUTs to your update action.
This is difficult to help you because we don't know what you're trying to do. For example, it would be helpful if you showed more of the test (for example, the values you set as your variables) and the specific results of the test.
Anyway, is the #tags variable an arel object? and if so, are you expecting the ID as the value to be passed? If not, then you probably want to specify the attribute referenced in #tags. For example, #tags.name... Or, does #tags reference a hash, itself?

Create record example for Quickbooks Online and Intuit Anywhere using Ruby and httparty?

Can someone post an example of creating a record in quickbooks online / intuit anywhere, using ruby and httparty?
I am working on an integration to a ruby on rails app using intuit anywhere, and am running into an issue with my POST request when attempting to create a new record. I have been able to successfully retrieve data (customers) using a POST command that doesn't require XML data in the body of the request, but am running into trouble when trying to create new records that have required fields that need to be passed in XML in the body of the request.
I get the same flavor of error in any entity for which I try to create a record for: an invalid or missing required field. It seems to me that the XML in the body (where the data for the required fields is added) is either being ignored (incorrect formatting?) or is not being attached.
I was hoping the someone else familiar with ruby could post an example of a record creation using httparty. If I could see how to correctly pass the XML using httparty, I can fix my problem myself.
I have been using the customer.com example (https://code.intuit.com/integration/viewvc/viewvc.cgi/IntuitAnywhere-Ruby/customer.com/?root=intuitanywhere&system=exsy1003) mostly as posted, with a few irrelevant modifications needed to get it to work in Rails 3.1. I am using the data pull and handling provided in the example, which looks like a pretty standard API wrapper built using httparty.
I am using a pull similar to the one found in the company_controller customers method. Here are two different ways I have tried submitting the XML:
#########################################
#Example 1 - XML
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}",
{ :body =>
"<Account xmlns:ns2=\"http://www.intuit.com/sb/cdm/qbo\" xmlns=\"http://www.intuit.com/sb/cdm/v2\">
<Name>Test Account 2</Name>
<Desc>Test Account</Desc>
<Subtype>Savings</Subtype>
<AcctNum>5001</AcctNum>
<OpeningBalanceDate>2010-05-14</OpeningBalanceDate>
</Account>",
:headers => {
"Content-Type" => "application/xml"
}}
)
#########################################
#Example 2 - hash
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}",
{ :body => {
:Account => {
:Name => "Loan Account 2",
:Desc => "Loac Account 2",
:Subtype => "Savings",
:AcctNum => "5001",
:OpeningBalanceDate => "2011-04-22"
}
},
:headers => {
"Content-Type" => "application/xml"
}}
)
I incorrectly assumed the customer.com example provided by intuit was using the httparty gem to make the POST call, so I was using the wrong syntax. They are actually using the OAuth gem's POST call, who's syntax can be found here: http://oauth.rubyforge.org/rdoc/classes/OAuth/AccessToken.html
I also had to modify the headers to get the Intuit Anywhere service to accept the XML body. Here is the code that finally worked for me to create a record in quickbooks online using intuit anywhere:
e = #company.intuit_token.post("https://qbo.intuit.com/qbo1/resource/account/v2/#{#company.realm}", "<Account xmlns:ns2=\"http://www.intuit.com/sb/cdm/qbo\" xmlns=\"http://www.intuit.com/sb/cdm/v2\"><Name>Test Account </Name><Desc>Test Account</Desc><Subtype>Savings</Subtype><AcctNum>5002</AcctNum><OpeningBalanceDate>2010-05-14</OpeningBalanceDate></Account>", {"Content-Type" => "application/xml", "standalone" => "yes", "encoding" => "UTF-8"})

Manually filter parameters in Rails

How would I go about manually filtering a hash using my application's parameter filter?
I imagine it'd go like this:
Rails.application.filter :password => 'pass1234'
# => {:password => '[FILTERED]'}
EDIT (clarification): I'm aware that Rails filters the params hash when writing to the logs. What I want to do is apply that same filter to a different hash at my prerogative before writing it to the logs with something like Rails.logger.info. I'm calling a remote HTTP query as a part of my application (since most of the backend operates through a remote API), and I'm logging the URL and parameters passed. I want to have the logs but also ensure that none of the sensitive params show up there.
After a few minutes of shotgunning it, I figured out this was the way to do it:
filters = Rails.application.config.filter_parameters
f = ActionDispatch::Http::ParameterFilter.new filters
f.filter :password => 'haha' # => {:password=>"[FILTERED]"}
See the config/application.rb file, towards the end there is a line:
config.filter_parameters += [:password]
This way the "password" param will not be shown in logs, but you can still access the value normally.
Edit
It seem that have misunderstood your meaning of "filter" originally. As for the clarified issue, I have no idea on how to handle it the truly Rails way.
Here is a brute force approach:
Parse the query with CGI::parse(URI.parse(my_url_address_with_params).query) to get a hash of param/values (note: values are actually stored as an array; here is the discussion).
Locate the parameters you want to filter out and replace values with literal *filtered*.
Call Rails.logger.info (or debug) directly to log.
Here is what you should dig into when relying on Rails magical classes and methods:
In Rails 3 the code that does the trick seems to live in ActionDispatch::Http (ParameterFilter in particular, method `filtered_parameters'). The documentation is available at API Dock (or, to be honest, very little documentation). You can examine the sources to get an idea of how this works.
My knowledge of Rails internals is not good enough to suggest anything else. I believe that someone with a better understanding of it might be of more help.
Building on Steven Xu's answer above, I made this initializer in my rails app:
class ActionController::Parameters
def filtered
ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters).filter(self)
end
end
Which let's me call params.filtered
[1] pry(#<LessonsController>)> params.filtered
{
"controller" => "lessons",
"action" => "search",
"locale" => "en"
}
[2] pry(#<LessonsController>)> params[:password] = "bob"
"bob"
[3] pry(#<LessonsController>)> params.filtered
{
"controller" => "lessons",
"action" => "search",
"locale" => "en",
"password" => "[FILTERED]"
}

rails mailer with different layouts

I use one layout for all my emails in my Notifier model (20+ emails)... however sometimes I just want to send a plain text email with no layout or html at all. I can't seem to be able to figure out how? If I try to send a plain text email i still get the layout, and all the HTML in the email.
I'm using Rails 2.3.8.
I read about this monkey patch here... but it seemed to indicate a newer version of rails had over come this? And I don't really wanna monkey patch if I can avoid one.
Rails - setting multiple layouts for a multipart email with mailer templates
layout "email" # use email.text.(html|plain).erb as the layout
def welcome_email(property)
subject 'New Signup'
recipients property.email
from 'welcome#test.com'
body :property => property
content_type "text/html"
end
def send_inquiry(inquire)
subject "#{inquire.the_subject}"
recipients inquire.ob.email
from "Test on behalf of #{inquire.name} <#{inquire.email}>"
body :inquire => inquire
content_type "text/plain"
end
I also have 2 files.
email.text.html.erb
email.text.plain.erb
It always uses text.html.erb... even if the content_type is "text/plain"
edit: Figured it out, the layouts follow a different naming scheme to the email templates. Just rename them as follows:
layout.text.html.erb => layout.html.erb
layout.text.plain.erb => layout.text.erb
I also made the mistake of manually defining the parts, if you use this:
part :content_type => 'text/plain',
:body => render_message('my_template')
Then Rails can't determine the content_type for your part and it assumes it's HTML.
After I changed those two things it worked for me!
original reply follows..
I've struggled with this question many times in the past, usually ending up with some sort of non-dry quick and dirty solution. I always thought I was the only one with this problem because Google turns up exactly nothing useful on the subject.
This time I decided to dig into Rails to figure it out but so far without much success, but maybe my findings will help someone else figure this out.
What I found was that in ActionMailer::Base the #render_message method is tasked with determining the proper content_type and should assign it to #current_template_content_type. #default_template_format then either returns the proper mime type for the layout or, if #current_template_content_type isn't set, it will default to :html.
This is what ActionMailer::Base#render_message looks like in my app (2.3.5)
def render_message(method_name, body)
if method_name.respond_to?(:content_type)
#current_template_content_type = method_name.content_type
end
render :file => method_name, :body => body
ensure
#current_template_content_type = nil
end
The trouble is that method_name appears to be a string (the name of the local view, in my case "new_password.text.html") and strings of course do not respond_to #content_type, meaning #current_template_content_type will always remain nil, and so the #default_template_format will always default to :html.
Not much closer to an actual solution, I know. The ActionMailer internals are just too opaque for me.
OK, not sure if this works, but it seems the default content_type is text/plain, so you would only need to set the content type if you want something other than text/plain.
Try this:
def send_inquiry(inquire)
subject "#{inquire.the_subject}"
recipients inquire.ob.email
from "Test on behalf of #{inquire.name} <#{inquire.email}>"
body :inquire => inquire
end
I still think you should consider this:
layout "email", :except => [:send_inquiry]
I would use the above because the plain text email does not seem to have a 'layout', only the actual content you want to send.
I found this that I think could be useful.
http://blog.blazingcloud.net/2009/11/17/simple-email-form-with-actionmailer/
He makes use of renaming the view templates for different content types.

Resources