I’m using the Ruby SDK aws-sdk-dynamodb with Ruby 2.5 for an AWS Lambda function that saves an item to an AWS DynamoDB table.
I can successfully save an item with this code:
def save!
hash = {
table_name: ‘my-table’,
item: {
message_sid: '123456',
created_at: Time.now.to_s
}
}
dynamo = Aws::DynamoDB::Client.new(region: ‘us-east-1’)
dynamo.put_item(hash)
puts 'item successfully saved'
true
rescue => error
puts "Unable to save item: #{error}: #{error.message}"
false
end
I get an error “no such member :message_sid” when I use this code:
def save!
dynamoDB = Aws::DynamoDB::Resource.new(region: ‘us-east-1’)
table = dynamoDB.table(‘my-table’)
hash = { message_sid: '123456', created_at: Time.now.to_s }
table.put_item(hash)
puts 'item successfully saved'
true
rescue => error
puts "Unable to save item: #{error}: #{error.message}"
false
end
I haven’t found any DynamoDB documentation for the error “no such member”. Why does the second example fail?
Admittedly the error message is not very helpful but a close reading of the example documentation reveals that DynamoDB expects the key item: when the method put_item is called on a table object. So this code will work:
def save!
dynamoDB = Aws::DynamoDB::Resource.new(region: ‘us-east-1’)
table = dynamoDB.table(‘my-table’)
hash = { message_sid: '123456', created_at: Time.now.to_s }
table.put_item(item: hash)
puts 'item successfully saved'
true
rescue => error
puts "Unable to save item: #{error}: #{error.message}"
false
end
Specifically you should use table.put_item(item: hash) not table.put_item(hash).
Related
I create this code, using this reference https://developers.google.com/drive/api/quickstart/ruby
class Drive
def initialize
#drive_service = Google::Apis::DriveV3::DriveService.new
#drive_service.client_options.application_name = APPLICATION_NAME
#drive_service.authorization = authorize
end
def create_file
data = { 'name': "My new Sheet #{Time.now.strftime('%d/%m/%Y %H:%M')}",
'mimeType': 'application/vnd.google-apps.spreadsheet' }
#drive_service.create_file(data).execute
end
def share_file
"to be filled"
end
def list_files
response = #drive_service.list_files(page_size: 10, fields: 'nextPageToken, files(id, name)')
puts 'Files:'
puts 'No files found' if response.files.empty?
response.files.each do |file|
puts "#{file.name} (#{file.id})"
end
end
end
The method list_files works well, but the create_file return me this error:
Traceback (most recent call last):
2: from quickstart.rb:79:in `<main>'
1: from quickstart.rb:60:in `create_file'
/home/vagrant/.rvm/gems/ruby-2.7.2/gems/google-api-client-0.53.0/generated/google/apis/drive_v3/service.rb:895:in `create_file': unknown keywords: :name, :mimeType (ArgumentError)
I created it based on this create method reference: https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/DriveV3/DriveService.html#create_file-instance_method
but I still can't get it to work, what could be wrong?
I tried changing the creation to: #drive_service.create_file(file_object = data).execute
Let's compare your code,
data = { 'name': "My new Sheet ...
#drive_service.create_file(data).execute
vs. the documented method signature.
`create_file(file_object = nil, enforce_single_parent: nil, ignore_default_visibility: nil, include_permissions_for_view: nil, keep_revision_forever: nil, ocr_language: nil, supports_all_drives: nil, supports_team_drives: nil, use_content_as_indexable_text: nil, fields: nil, quota_user: nil, user_ip: nil, upload_source: nil, content_type: nil, options: nil) {|result, err| ... } ⇒ Google::Apis::DriveV3::File`
- file_object (Google::Apis::DriveV3::File) (defaults to: nil)
The first argument should be an instance of Google::Apis::DriveV3::File, but you are passing a Hash.
According to this documentation:
When supplying hashes for request objects. If it is the last argument
to a method, some versions of Ruby will interpret the hash as keyword
arguments. To prevent this, appending an empty hash as an extra
parameter will avoid misinterpretation.
file = {id: '123', title: 'My document', labels: { starred: true }}
file = drive.create_file(file) # Raises ArgumentError: unknown keywords: id, title, labels
file = drive.create_file(file, {}) # Returns a Drive::File instance
In your code, just add {} as 2nd parameter for create_file.
From:
#drive_service.create_file(data)
To:
#drive_service.create_file(data, {})
Let me know if this works on your end.
I've got simple method which fetch data from Jira project with assigned tasks to user which left to be done. Is there any way to downsize method below to avoid rubocop condition size too high error?
def initialize
#project = Jira::ProjectConnection.new('project_key').call
end
def assigned_task_list
project.issues.map do |issue|
next unless issue.fields.dig('status', 'name') != 'Done' && !issue.fields.dig('assignee', 'name').nil?
{
key: issue.key,
name: issue.fields.dig('assignee', 'name'),
email: issue.fields.dig('assignee', 'emailAddress'),
status: issue.fields.dig('status', 'name')
}
end
end
Perhaps like this?
def assigned_task_list
assigned_tasks.map do |issue|
fields = issue.fields
{
key: issue.key,
name: fields.dig('assignee', 'name'),
email: fields.dig('assignee', 'emailAddress'),
status: fields.dig('status', 'name')
}
end
end
private
def assigned_tasks
project.issues.select do |issue|
issue.fields.dig('status', 'name') != 'Done' &&
issue.fields.dig('assignee', 'name')
end
end
I have a ruby application/website where users can scan qr codes and make donations to bitcoin wallets. Whenever a donations occurs a transaction and event is created and the bitcoins wallet value is updated. Here is the code below
require 'date'
module Server
class Event
include DataMapper::Resource
property :id, Serial
property :duration, Float, required: true, default: ->(x, y) { 1.0 }
property :created_at, DateTime, required: true, index: true, default: ->(x, y) { DateTime.now }
property :valve, Integer, required: true
belongs_to :transaction, index: true
belongs_to :bucket, index: true
def self.register(wallet, amount, hash)
bucket = Bucket.first(wallet: wallet)
unless bucket
raise DataMapper::ObjectNotFoundError, "Bucket not found for wallet #{wallet}"
end
tr = Transaction.create!({
wallet: wallet,
amount: amount,
thash: hash,
created_at: DateTime.now
})
ev = Event.create!({
valve: bucket.valve,
duration: calculate_duration(amount * bucket.multiplier),
transaction: tr,
bucket: bucket,
created_at: tr.created_at
})
bucket.update(amount: bucket.amount + amount)
end
def amount
self.transaction { |x| x.amount }
end
def self.calculate_duration(amount)
[[amount, Config.server.min_duration].max, Config.server.max_duration].min
end
def simple
return {
valve: valve,
duration: duration_millis
}
end
def duration_millis
(duration * 1000).to_i
end
def to_hash(is_simple)
if is_simple
simple
else
attributes.merge simple
end
end
def self.latest(valve = nil)
if valve.nil?
Event.first(order: [:created_at.asc])
else
Event.first(order: [:created_at.asc], valve: valve)
end
end
def self.latest!(valve = nil)
result = self.latest(valve)
result.destroy unless result.nil?
result
end
end
end
There are 3 wallets total. so 0, 1 & 2. My question is how can I play a specific sound depending on what wallet is updated using an if statement after the bucket update function is called.
I'm using the following code to generate a JSON file containing all category information for a particular website.
require 'mechanize'
#hashes = []
#categories_hash = {}
#categories_hash['category'] ||= {}
#categories_hash['category']['id'] ||= {}
#categories_hash['category']['name'] ||= {}
#categories_hash['category']['group'] ||= {}
# Initialize Mechanize object
a = Mechanize.new
# Begin scraping
a.get('http://www.marktplaats.nl/') do |page|
groups = page.search('//*[(#id = "navigation-categories")]//a')
groups.each_with_index do |group, index_1|
a.get(group[:href]) do |page_2|
categories = page_2.search('//*[(#id = "category-browser")]//a')
categories.each_with_index do |category, index_2|
#categories_hash['category']['id'] = "#{index_1}_#{index_2}"
#categories_hash['category']['name'] = category.text
#categories_hash['category']['group'] = group.text
#hashes << #categories_hash['category']
# Uncomment if you want to see what's being written
puts #categories_hash['category'].to_json
end
end
end
end
File.open("json/magic/#{Time.now.strftime '%Y%m%d%H%M%S'}_magic_categories.json", 'w') do |f|
puts '# Writing category data to JSON file'
f.write(#hashes.to_json)
puts "|-----------> Done. #{#hashes.length} written."
end
puts '# Finished.'
But this code returns a JSON file filled with just the last category data. For the full JSON file take a look here. This is a sample:
[
{
"id":"36_17",
"name":"Overige Diversen",
"group":"Diversen"
},
{
"id":"36_17",
"name":"Overige Diversen",
"group":"Diversen"
},
{
"id":"36_17",
"name":"Overige Diversen",
"group":"Diversen"
}, {...}
]
The question is, what's causing this and how can I solve it?
The same object, the result of #categories_hash['category'], is being updated each loop.
Thus the array is filled with the same object 1747 times, and the object reflects the mutations done on the last loop when it is viewed later.
While a fix might be to use #categories_hash[category_name] or similar (i.e. fetch/ensure a different object each loop), the following avoids the problem described and the unused/misused hash of 'category' keys.
categories.each_with_index do |category, index_2|
# creates a new Hash object
item = {
id: "#{index_1}_#{index_2}",
name: category.text,
group: group.text
}
# adds the new (per yield) object
#hashes << item
end
Alternatively, a more "functional" approach might be to use map, but it solves the problem in the same way - by creating new [Hash] objects. (This could be expanded to also include the outer loop, but it's just here for a taste.)
h = categories.each_with_index.map do |category, index_2|
{
id: "#{index_1}_#{index_2}",
name: category.text,
group: group.text
}
end
#hashes.concat(h)
I need to receive all abilities for current_user in :json for my backbone app. So the firs idea was to add some think like this:
def receive_user_abilities # we will return onty hash for works and tasks
w = Work.accessible_by(current_ability).map { |w| w = {type: 'Work', id: w.id}) }
t = Task.accessible_by(current_ability).map { |t| t = {type: 'Task', id: t.id}) }
render json: t + w # returs merged hash
end
But both line are particular the same, and I decided to user some metaprograming magic. So my solution was to create new helper, include it to my controller, and pass *arg to newly created module(helper) method. Here it is:
module AbilitiesHelper
def receive_abilities_for *classes
classes.inject([]) { |res, klass| res + eval( klass.to_s.capitalize + '.accessible_by(current_ability).map { |element| element = ({type: ' + klass.to_s.capitalize + ', id: element.id }) }') }
end
end
and here is new call from controller
def receive_user_abilities
render json: receive_abilities_for(:work, :task) # returs merged hash
end
It's basically the same, but for some reason I receive an error SystemStackError - stack level too deep:
Where is the error??
Maybe this approach would be easier?
def receive_abilities_for *classes
classes.inject([]) do |res, klass|
res + klass.accessible_by(current_ability).map do |element|
element = {type: klass.to_s, id: element.id }
end
end
end
And call this method in such way:
def receive_user_abilities
render json: receive_abilities_for(Work, Task)
end
Also, as for me, receive_abilities_for method isn't metaprogramming. Metaprogramming is defining new methods and classes at runtime (I may be mistaken).