Mongoid Is it possible to override updated_at timestamp - ruby

I am trying to manually edit the "updated_at" field through a rake task
Here is what it looks like:
task :campaigns_updated_at_recovery => :environment do
Dir.foreach('db/raw-data/campaigns/') do |json|
next if json == '.' or json == '..'
file = File.read('db/raw-data/campaigns/'+json)
data_hash = JSON.parse(file)
#p data_hash["_id"]
thisCampaign = Campaign.find(data_hash["_id"])
thisCampaign["channels"].each do |chan|
if chan["updated_at"] < Date.new(2018,04,19)
data_hash["channels"].each do |channel|
if chan["_id"].to_s == channel["_id"]
chan["updated_at"] = Date.parse(channel["updated_at"])
end
end
end
thisCampaign.save
end
end
However when I run this task, the updated_at date is either not changed or updated to today's date.
What am I missing ?
I am using Mongoid and not ActiveRecord by the way

updated_at is updated by mongoid itself in a callback.
You have two solution to work around that.
The easiest solution would be to use set to change the value directly without firing any callback:
thisCampaign.set(channels: thisCampaign['channels'])
The more flexible solution would be to go down to the driver level. The basic idea is:
Campaign.collection.find(_id: data_hash["_id"]).update_one({
'$set' => {
updated_at: yourDate
}
})
Given your example, you would first need to get the full document
thisCampaign = Campaign.collection.find(_id: data_hash["_id"]).first
if thisCampaign
thisCampaign["channels"].each do |chan|
if chan["updated_at"] < Date.new(2018,04,19)
data_hash["channels"].each do |channel|
if chan["_id"].to_s == channel["_id"]
chan["updated_at"] = Date.parse(channel["updated_at"])
end
end
end
end
Campaign.collection.find(_id: data_hash["_id"]).update_one({
'$set' => {channels: thisCampaign["channels"]}
})
end

Related

Ruby If statement

I am trying to do a post and run some if statement. What I want to do is:
check all fields are filled
if all fields are filled move on to next step, or else reload page
check if already in data base
add if not already in data base
post "/movies/new" do
title = params[:title]
year = params[:year]
gross = params[:gross]
poster = params[:poster]
trailer = params[:trailer]
if title && year && gross && poster && trailer
movie = Movie.find_by(title: title, year: year, gross: gross)
if movie
redirect "/movies/#{movie.id}"
else
movie = Movie.new(title: title, year: year, gross: gross, poster: poster, trailer: trailer)
if movie.save
redirect "/movies/#{movie.id}"
else
erb :'movies/new'
end
end
else
erb :'movies/new'
end
end
I don't think my if statement is correct. It works even if all my fields are not filled
Your code is doing a lot of work in one single method. I would suggest to restructure it into smaller chunks to make it easier to manage. I mostly code for Rails, so apologies if parts of these do not apply to your framework.
post "/movies/new" do
movie = find_movie || create_movie
if movie
redirect "/movies/#{movie.id}"
else
erb :'movies/new'
end
end
def find_movie
# guard condition to ensure that the required parameters are there
required_params = [:title, :year, :gross]
return nil unless params_present?(required_params)
Movie.find_by(params_from_keys(required_params))
end
def create_movie
required_params = [:title, :year, :gross, :poster, :trailer]
return nil unless params_present?(required_params)
movie = Movie.new(params_from_keys(required_params))
movie.save ? movie : nil # only return the movie if it is successfully saved
end
# utility method to check whether all provided params are present
def params_present?(keys)
keys.each {|key| return false if params[key].blank? }
true
end
# utility method to convert params into the hash format required to create / find a record
def params_from_keys(keys)
paras = {}
keys.each { |key| paras.merge!(key: params[key]) }
paras
end
Even if you type nothing in the HTML fields, they will still be submitted as empty strings.
You can avoid having empty parameters by, for example, filtering them:
post '/movies/new' do
params.reject! { |key, value| value.empty? }
# rest of your code
end
Also I would rather post to /movies rather than to /movies/new, that's more REST-wise.
Try if condition to check fields are blank like below -
unless [title, year, gross, poster, trailer].any?(&:blank?)
This will check any of the field should not be nil or blank("").

Dynamically check if a field in JSON is nil without using eval

Here's an extract of the code that I am using:
def retrieve(user_token, quote_id, check="quotes")
end_time = Time.now + 15
match = false
until Time.now > end_time || match
#response = http_request.get(quote_get_url(quote_id, user_token))
eval("match = !JSON.parse(#response.body)#{field(check)}.nil?")
end
match.eql?(false) ? nil : #response
end
private
def field (check)
hash = {"quotes" => '["quotes"][0]',
"transaction-items" => '["quotes"][0]["links"]["transactionItems"]'
}
hash[check]
end
I was informed that using eval in this manner is not good practice. Could anyone suggest a better way of dynamically checking the existence of a JSON node (field?). I want this to do:
psudo: match = !JSON.parse(#response.body) + dynamic-path + .nil?
Store paths as arrays of path elements (['quotes', 0]). With a little helper function you'll be able to avoid eval. It is, indeed, completely inappropriate here.
Something along these lines:
class Hash
def deep_get(path)
path.reduce(self) do |memo, path_element|
return unless memo
memo[path_element]
end
end
end
path = ['quotes', 0]
hash = JSON.parse(response.body)
match = !hash.deep_get(path).nil?

How to get more records out of the Twitter gem?

I'm banging my head trying to understand how the Twitter gem's pagination works.
I've tried max_id and cursor and they both strangely don't work.
Basically the maximum I can get out of search results is 100, and I would like to get 500.
Current code:
max_page = 5
max_id = -1
#data = []
for i in (1..max_page)
t = twt_client.search("hello world", :count => 100, :result_type => :recent, :max_id => max_id)
t.each do | tweet |
#data << tweet
end
max_id = t.next_results[:max_id]
end
This actually tells me that next_results is a private method, anyone has a working solution?
Without knowing which gem you're referencing (please specify a URL), I'd say intiuitively that cursor and max_id wouldn't get you what you want. However count would. Since you say you're only retrieving 100 results and count is set to 100, that would make sense to me.
t = twt_client.search("hello world", :count => 500, :result_type => :recent, :max_id => max_id)
I'm assuming you're talking about the Twitter client referenced here. My first question is: What's twt_client and for that matter, what does its search method return? It's also possible that you've unwittingly updated the gem and there's been a code base change that makes your current script out of date.
Take a look at your installed gem version and another look at the README here.
Twitter::SearchResults#next_results is private, because they try to provide uniform interface for enumeration.
Look, there's included Twitter::Enumerable in search_results.rb
module Twitter
class SearchResults
include Twitter::Enumerable
...
private
def last?
!next_page?
end
...
def fetch_next_page
response = #client.send(#request_method, #path, next_page).body
self.attrs = response
end
...
end
end
And if you look at enumerable.rb, you'll see that method's Twitter::SearchResults#last? and Twitter::SearchResults#fetch_next_page are used by Twitter::SearchResults#each method
module Twitter
module Enumerable
include ::Enumerable
# #return [Enumerator]
def each(start = 0)
return to_enum(:each, start) unless block_given?
Array(#collection[start..-1]).each do |element|
yield(element)
end
unless last?
start = [#collection.size, start].max
fetch_next_page
each(start, &Proc.new)
end
self
end
...
end
end
And Twitter::SearchResults#each will iterate over pages until there's #attrs[:search_metadata][:next_results] in Twitter's responses. So you need to break iteration after you'll reach 500th element.
I think you just need to use each
#data = []
tweet_number = 1
search_results = twt_client.search("hello world", count: 100, result_type: :recent)
search_results.each do |tweet|
#data << tweet
break if tweet_number == 500
end
This post is a result of looking into gem's sources and twitter's api. I could make a mistake somewhere, since I haven't checked my thoughts in console.
Try this (I basically only updated the calculation of the max_id in the loop):
max_page = 5
max_id = -1
#data = []
for i in (1..max_page)
t = twt_client.search("hello world", :count => 100, :result_type => :recent, :max_id => max_id)
t.each do | tweet |
#data << tweet
end
max_id = t.to_a.map(&:id).max + 1 # or may be max_id = t.map(&:id).max + 1
end

Feed Array of Strings into Ruby Loop

I'd like to write a quick method that can help me initialize a few fields in one of my ruby tables. This is what I have so far but it does not work. I would like to be able to feed an array of field names into this function so that I can get the whole initialization done in one loop.
fields =["field1","field2","field3","field4"]
tasks = Task.all
tasks.each do |task|
fields.each do |field|
if task.field.nil?
task.update_attribute :field => true
end
end
end
Maybe this is what you mean:
fields = %w[field1 field2 field3 field4]
tasks = Task.all
tasks.each do |task|
fields.each do |field|
task.update_attribute :"#{field}" => true if task.send(field).nil?
end
end
If this is actually Rails, as it appears to be, you can use hash access:
task[field] = true if task[field].nil?
You would still need to save the modified record.
You may use task.update_attribute(field, true) instead: this will update the database immediately, but will do a transaction for each modified attribute.
Try to always use the least number of queries to the database
fields = ["field1","field2","field3","field4"]
fields.each do |field|
Task.where({field => nil}).update_all({field => true})
end

Rho Mobile: Parse and create a model using JSON

I am able to parse a JSON using the following code
$httpresult = #params['body']
$jsonresult = Rho::JSON.parse($httpresult)
But I don't know how to create a model from $jsonresult.
First, using app_info you can print the result coming from the server to check if the response is valid JSON string.
Second, i think you must decode the url in order to parse it by using:
Rho::JSON.parse(Rho::RhoSupport.url_decode(#params['body']))
Once you've the data in json_result, you can put them in a pre-existing Model.
Supposing that you've already created a model with the name "Product", you can use transactions to speed up the process.
At the beginning of your module you've to require the model name:
require_source 'Product'
Then you can do this callback:
def get_callback
if #params['status'] == "ok"
json_result = Rho::JSON.parse(#params['body'])
db = ::Rho::RHO.get_src_db('Product')
db.start_transaction
Product.delete_all
begin
json_result.each do |item|
Product.create({:Brand => item["B rand"], :Name => item["Name"], :SKU => d["SKU"]})
end
db.commit
rescue Exception => e
trace_msg = e.backtrace.join("\n")
puts 'Application initialize failed: ' + e.inspect + ";Trace: #{trace_msg}"
db.rollback
end
WebView.navigate Rho::RhoConfig.start_path
else
WebView.navigate url_for :action => :show_error
end
end

Resources