How to retrieve reference to AWS::S3::MultipartUpload with ruby - ruby-on-rails

I have a rails app and in a controller action I am able to create a multipart upload like so:
def create
s3 = AWS::S3.new
bucket = s3.buckets["my_bucket"]
key = "some_new_file_name.ext"
obj = bucket.objects[key]
mpu = obj.multipart_upload
render json: {
:id => mpu.id
}
end
so now the client has the multipart upload id and she can upload parts to aws with her browser. I wish to create another action which will assemble the parts once they are done uploading. Something like:
def assemble
s3 = AWS::S3.new
bucket = s3.buckets["my_bucket"]
key = params['key']
bucket.objects[key].multipart_upload.complete
render json: { :status => "all good" }
end
This isn't working though. How do I retrieve a reference to the multipartUpload object or create a new one with the key or id so that I can call the "complete" method on it? Any insight is appreciated

I found this method in the documentation for the Client class and got it to work as follows:
client = AWS::S3::Client.new
# reassemble partsList
partsList = []
params[:partsList].each do |key, pair|
partsList.push pair
end
options = {
:bucket_name => 'my_bucket',
:key => params[:key],
:upload_id => params[:upload_id],
:parts => partsList
}
client.complete_multipart_upload(options)

Related

Is there a way I can use a rake task or method that can rewrite an HTML partial using erb?

Sorry for the confusing title, but I will elaborate here.
ok so on the users index page of my site I have a list of Top Trending songs. The list is ordered by user rankings and this list changes dynamically as each songs aggregate ranking changes relative to each other.
class SongratingsController < ApplicationController
#Top100 = Rails.cache.read('Top100')
lastSpot = #Top100.last
def reCalcTop100
#Top100 = Song.where('num_stars > ?', 0 ).order('num_stars desc, total_score desc').limit(100)
Rails.cache.fetch('Top100'){#Top100}
end
def addRatingToSong
userID = params[:uid].to_i
songId = params[:sid].to_i
rVal = params[:valR].to_i
#averageS = []
songRate = Songrating.find_by(:user_id => userID, :song_id => songId)
if songRate != nil
oldScore = songRate.rating
songRate.update_attributes(:rating => rVal)
#song = Song.find(songId)
score = #song.total_score - oldScore
newScore = score + rVal
averageScore = newScore/#song.songratings_count
#song.update_attributes(:total_score => newScore,:num_stars => averageScore)
#averageS[0] = averageScore
#averageS[1] = #song.songratings_count
else
Songrating.create!(:user_id => userID, :song_id => songId,:rating => rVal)
#song = Song.find(songId)
newScore = #song.total_score + rVal
averageScore = newScore/#song.songratings_count
#song.update_attributes(:total_score => newScore,:num_stars => averageScore)
#averageS[0] = averageScore
#averageS[1] = #song.songratings_count
end
if newScore > lastSpot.total_score && averageScore > lastSpot.num_stars
reCalcTop100
end
if request.xhr?
render :json => {
:starData => #averageS
}
end
end
end
As you can see in these two photos below I have a view partial that shows this list, but right now I have the list generated each time a user logs into the main page. But since this list is not unique to the user, I feel I am wasting time regenerating this list.
ideally I would like to generate and write a static HTML partial only when the top100 list changes, but I don't really know how to accomplish this.
thanks.
Yep just use erb
vars = OpenStruct.new({some_var: some_val})
rendered_html = ERB.new(File.read("#{Rails.root}/app/templates/embed_code.html.erb")).result(vars.instance_eval { binding })
This will put the rendered html in the rendered_html variable from there you can write it to a file or do anything you want. This should work in the context of a ruby class or rake task afaik.
The vars are passed to the template and can be used as <%= some_var %> in the template.
Now that i've answered you actual question, i think the better solution is to just use Rails.cache to cache the rendered data.
Anything that takes a long time can be cached with
result = Rails.cache.fetch "some_cache_key" do
# things you want to cache
end
this will cache the block and return it to result. if unstale cached data exisits in the future it will just return it from cache, if cache is empty or stale it will re-execute the block and return it into result.
Finally in the context fo a controller you can just use action caching which is a bit more hands off.
See: http://guides.rubyonrails.org/caching_with_rails.html for more details.

Data from a JSON API combined with local database in Rails

I would like to combine data retrieved from an API with local database data in a new JSON. But I think I'm doing this wrong. Here is my code :
#data = ActiveSupport::JSON.decode(#api_data)
#data.each do |key|
if key['state'] == "active"
user_id = key['id']
user_database = User.where(:user_id => user_id).take
#userlist = []
unless user_database.blank?
user_data = {
:user_id => key['id'],
:enrolement_start_date => key['start_at'],
:enrolement_end_date => key['end_at'],
:user_interest => user_database.interests,
:user_discipline_id => user_database.discipline_id,
}
#userlist.push(user_data)
end
end
end
#userlist = #userlist.to_json
Actually, it's working but I only receive the last user as result. I don't figure how to make it works :-/ Many thanks in advance !
#userlist = []
that line, each time through the loop
#data.each do |key|
is clearing out the previous data by re-initializing #userlist. Then you're returning the user you added last, since all the others were thrown away the last time you assigned an empty array to #userlist.
Just move
#userlist = []
above
#data.each do |key|
and you should be good.

Using result of `JSON.decode` in Rails

So I have an action in my controller that does a get_response to an API:
def memeapi
require "net/http"
require "uri"
#meme = Meme.new(params[:meme])
url = "http://version1.api.memegenerator.net/Instance_Create?username=apigen&password=SECRET&languageCode=en&generatorID=#{#meme.memeid}&imageID=#{#meme.imgid}&text0=#{#meme.text0}&text1=#{#meme.text1}"
resp = Net::HTTP.get_response(URI.parse(url))
data = resp.body
# I want to convert it to the Rails data structure - a hash
result = ActiveSupport::JSON.decode(data)
end
Ok but now I want to get back the information, use it to create another object, but I cant even format the information I am getting, can anyone tell me what I am doing wrong, what am I missing?
I want to be able to access the information from the get_response...
Thank you.
This is the JSON structure
{"success":true,"result":{"generatorID":45,"displayName":"Insanity Wolf","urlName":"Insanity-Wolf","totalVotesScore":0,"imageUrl":"/cache/images/400x/0/0/20.jpg","instanceID":13226270,"text0":"push","text1":null,"instanceImageUrl":"/cache/instances/400x/12/12916/13226270.jpg","instanceUrl":"http://memegenerator.net/instance/13226270"}}
I dont want to save all the fields btw...
result will look something like this after JSON.decode in your code.
{
'success' => true,
'result' => {
"generatorID" => 45,
"displayName" => "Insanity Wolf",
"urlName" => "Insanity-Wolf",
"totalVotesScore" => 0,
"imageUrl" => "/cache/images/400x/0/0/20.jpg",
"instanceID" => 13226270,
"text0" => "push",
"text1" => nil,
"instanceImageUrl" => "/cache/instances/400x/12/12916/13226270.jpg",
"instanceUrl" => "http://memegenerator.net/instance/13226270"
}
}
You have a nested hash (hash as part of a hash) here, which you can access like this:
image_url = result['result']['imageUrl'] # => "/cache/images/400x/0/0/20.jpg"
What you actually want to do with this information, I cannot guess. Maybe you want to update the Meme object you created?
#meme.url = image_url

Using a method while looping through an array in ruby

I am using ruby-aaws to return Amazon Products and I want to enter them into my DB. I have created a model Amazonproduct and I have created a method get_amazon_data to return an array with all the product information. When i define the specific element in the array ( e.g. to_a[0] ) and then use ruby-aaws item_attributes method, it returns the name I am searching for and saves it to my DB. I am trying to iterate through the array and still have the item_attributes method work. When i don't define the element, i get this error: undefined method `item_attributes' for #Array:0x7f012cae2d68
Here is the code in my controller.
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each { |name|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = #arr.item_attributes.title.to_s
}
EDIT: Code in my model to see if that helps:
class Amazonproduct < ActiveRecord::Base
def self.get_amazon_data(r)
resp = Amazon::AWS.item_search('GourmetFood', { 'Keywords' => 'Coffee Maker' })
items = resp.item_search_response.items.item
end
end
Thanks for any help/advice.
I'm not familiar with the Amazon API, but I do observe that #arr is an array. Arrays do not usually have methods like item_attributes, so you probably lost track of which object was which somewhere in the coding process. It happens ;)
Try moving that .item_attributes call onto the object that supports that method. Maybe amazonproduct.get_amazon_data(:r), before its being turned into an array with to_a, has that method?
It's not quite clear to me what your classes are doing but to use #each, you can do something like
hash = {}
[['name', 'Macbook'], ['price', 1000]].each do |sub_array|
hash[sub_array[0]] = sub_array[1]
end
which gives you a hash like
{ 'name' => 'Macbook', 'price' => 1000 }
This hash may be easier to work with
#product = Product.new
#product.name = hash[:name]
....
EDIT
Try
def create
#arr = Amazonproduct.get_amazon_data( :r ).to_a
#arr.each do |aws_object|
#amazonproduct = Amazonproduct.new(params[:amazonproducts])
#amazonproduct.name = aws_object.item_attributes.title.to_s
end
end

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