Check directory for files, retrieve first file - ruby

I'm writing a small ruby daemon that I am hoping will do the following:
Check if a specific directory has files (in this case, .yml files)
If so, take the first file (numerically sorted preferrably), and parse into a hash
Do a 'yield', with this hash as the argument
What I have right now is like:
loop do
get_next_in_queue { |s| THINGS }
end
def get_next_in_queue
queue_dir = Dir[File.dirname(__FILE__)+'/../queue']
info = YAML::load_file(queue_dir[0]) #not sure if this works or not
yield info
end
I'd like to make the yield conditional if possible, so it only happens if a file is actually found. Thanks!

Okay, I got this working!
The problem with queue_dir.empty? is that a directory always contains [".", ".."]
So what I did was:
def get_next_in_queue
queue_dir = Dir.entries(File.dirname(__FILE__)+'/../queue')
queue_dir.delete "."
queue_dir.delete ".."
if !queue_dir.empty?
info = YAML::load_file("#{File.dirname(__FILE__)}/../queue/#{queue_dir[0]}")
yield stem_info
else
sleep(30) #since it is empty, we probably don't need to check instantly
end
end

Just add additional checks:
def get_next_in_queue
queue_dir = Dir[File.dirname(__FILE__)+'/../queue']
return if queue_dir.empty?
info = YAML::load_file(queue_dir[0]) #not sure if this works or not
yield info if info
end
Depending on your wanted behaviour, you can additionally raise an exception, log an error, sleep for N seconds etc.

Related

Why negative value becomes positive after dumping into yaml file?

I have a simple sinatra app uses yaml files to handle data. One of the feature is an User can vote or veto for a Question. The vote feature works fine, but I met some weird things when implementing the veto feature.
Briefly saying:
when a question's current votes_count is positive (>= 1), the number will decrease correctly
but when a question's current votes_count is zero or negative, the number will successfully decrease in the data hash, but after dump data hash into the yaml file, negative becomes positive.
This is the yaml file for Question:
'1': !ruby/hash:Sinatra::IndifferentHash
title: " Best way to require all files from a directory in ruby?"
description: What's the best way to require all files from a directory in ruby ?
user_id: '3'
votes_count: 0
# other user information
This is the route handler relates to veto feature:
post "/questions/:id/veto" do
check_vote_validity_for_question(params[:id])
#question = Question.find_by(:id, params[:id])
#question.votes_count = (#question.votes_count.to_i - 1)
Question.update(params[:id], votes_count: #question.votes_count )
# omit user related code
end
This is the update method:
def self.update(id, attrs)
data = load_data_of(data_name)
# binding.pry
obj_info = data[id]
attrs.each do |k, v|
v = v.to_s if v.is_a?(Array)
obj_info[k] = v
end
# binding.pry
File.open(File.join(data_path, "#{data_name.to_s}.yaml"), "w+") do |f|
f.write(Psych.dump(data).delete("---"))
end
end
If I pause the program inside update method before and after update the data hash, it shows the value of votes_count was set correctly.
Before:
[1] pry(Question)> data
=> {"1"=>
{"title"=>" Best way to require all files from a directory in ruby?",
"description"=>"What's the best way to require all files from a directory in ruby ?",
"user_id"=>"3",
"votes_count"=>0},
After:
[1] pry(Question)> data
=> {"1"=>
{"title"=>" Best way to require all files from a directory in ruby?",
"description"=>"What's the best way to require all files from a directory in ruby ?",
"user_id"=>"3",
"votes_count"=>-1},
The value of the key"votes_count" in data hash is -1 after updating, but after I dumping the data hash into yaml file, the value of "votes_count" of the user in the yaml file became 1. And if the value in hash is -2, it will become 2 in the yaml file.
I tried making a hash which has a negative value in irb, then dump it into a yaml file, things work ok. I have no idea what happened. Could anybody help me?
It looks the problem in the line
f.write(Psych.dump(data).delete("---"))
You delete -.
For example
"-1".delete("---") #=> "1"

Chef: Count the number of files in a folder

I am trying to get the count of files in a folder and skip or execute the further resources based on the count.
file 'C:\Users\Desktop\Chef-file\count.txt' do
dir = 'C:\Users\Desktop\Chef-Commands'
count = Dir[File.join(dir, '**', '*')].count { |file| File.file?(file)}
content count
end
But getting the following error
Chef::Exceptions::ValidationFailed: Property content must be one of: String, nil! You passed 0.
I am pretty new to chef and ruby so was wondering how to fix/solve this problem.
Once the count is obtained, how to check its value in other resources?
Also would like to know if this is the right approach.
Thanks
count seems to be 0 (Fixnum).
You may wanna try:
file 'C:\Users\Desktop\Chef-file\count.txt' do
dir = 'C:\Users\Desktop\Chef-Commands'
count = Dir[File.join(dir, '**', '*')].count { |file| File.file?(file)}
content count.to_s
end

scanned files on array, would like to move them all into a folder with one code block

class Desktop
# grab the names of items on the desktop
desktop_content = Dir.entries("/Users/jorgeolivero/desktop")
def initialize(desktop_content)
#desktop_content = desktop_content
end
def initlialize(select)
arr.select { |x| x.include? "y"}
end
def initialize(desktop_files)
#desktop_files = desktop_files
end
# grabs the files from the desktop_content array
desktop_files = desktop_content.select { |c| c.include? '.'}
puts desktop_files
desktop_files.each { |files| File.rename('/Users/jorgeolivero/desktop/***feed array items here***', '/Users/jorgeolivero/desktop/folder/***feed array items here***') }
end
My guts tells me that this might look painful to some of you, so please know this is my first attempt at an app. So if you can remember that process, you understand how "stuck" I am right now.
I want to move all of the items in the "desktop_files" array into a folder with one command. That last piece of code shows how I am trying to do but I can't figure out how to feed each file name onto the end of each path (as denoted by the ***s).
Many thanks.
You can use the following code for copy all your files:
require 'fileutils'
FileUtils.mv Dir['/Users/jorgeolivero/desktop/*'], 'dest folder'
You don't need to create a class for it. You can run this code on irb or put in a single file.
PS: Ruby doesn't accept multiple initializers for a class.

How to override or edit the last printed lines in a ruby CLI script?

I am trying to build a script that gives me feedback about progress on the command-line. Actually it is just putting a newline for every n-th progress step made. Console looks like
10:30:00 Parsed 0 of 1'000'000 data entries (0 %)
10:30:10 Parsed 1'000 of 1'000'000 data entries (1 %)
10:30:20 Parsed 2'000 of 1'000'000 data entries (2 %)
[...] etc [...]
11:00:00 Parsed 1'000'000 of 1'000'000 data entries (100 %)
Even if timestamp and progressnumbers are fictional, you should see the problem.
What I want is to do it "wget-style" with a progressbar updated on the command line, with linewidth in mind.
First I thought about the use of curses because I had hands on as I tried to learn C, but I never could get warm with it, also I think it is bloated for the purpose of manipulating just a few lines. Also I dont need any coloring. Also most other libraries I found seemed to be specialized for coloring.
Can someone help me with this problem?
A while ago I created a class to be a status text on which you can change part of the content of the text within the line. It might be useful to you.
The class with an example use are:
class StatusText
def initialize(parms={})
#previous_size = 0
#stream = parms[:stream]==nil ? $stdout : parms[:stream]
#parms = parms
#parms[:verbose] = true if parms[:verbose] == nil
#header = []
#onChange = nil
pushHeader(#parms[:base]) if #parms[:base]
end
def setText(complement)
text = "#{#header.join(" ")}#{#parms[:before]}#{complement}#{#parms[:after]}"
printText(text)
end
def cleanAll
printText("")
end
def cleanContent
printText "#{#parms[:base]}"
end
def nextLine(text=nil)
if #parms[:verbose]
#previous_size = 0
#stream.print "\n"
end
if text!=nil
line(text)
end
end
def line(text)
printText(text)
nextLine
end
#Callback in the case the status text changes
#might be useful to log the status changes
#The callback function receives the new text
def onChange(&block)
#on_change = block
end
def pushHeader(head)
#header.push(head)
end
def popHeader
#header.pop
end
def setParm(parm, value)
#parms[parm] = value
if parm == :base
#header.last = value
end
end
private
def printText(text)
#If not verbose leave without printing
if #parms[:verbose]
if #previous_size > 0
#go back
#stream.print "\033[#{#previous_size}D"
#clean
#stream.print(" " * #previous_size)
#go back again
#stream.print "\033[#{#previous_size}D"
end
#print
#stream.print text
#stream.flush
#store size
#previous_size = text.gsub(/\e\[\d+m/,"").size
end
#Call callback if existent
#on_change.call(text) if #on_change
end
end
a = StatusText.new(:before => "Evolution (", :after => ")")
(1..100).each {|i| a.setText(i.to_s); sleep(1)}
a.nextLine
Just copy, paste in a ruby file and try it out. I use escape sequences to reposition the cursor.
The class has lots of features I needed at the time (like piling up elements in the status bar) that you can use to complement your solution, or you can just clean it up to its core.
I hope it helps.
In the meanwhile I found some gems that give me a progressbar, I will list them up here:
ProgressBar from paul at github
a more recent version from pgericson at github
ruby-progressbar from jfelchner at github
simple_progressbar from bitboxer at github
I tried the one from pgericson and that from jfelchner, they both have pros and cons but also both fits my needs. Probably I will fork and extend one of them in the future.
I hope this one helps others to find faster, what I searched for months.
Perhaps replace your outputting to this:
print "Progress #{progress_var}%\r"

Sinatra with a persistent variable

My sinatra app has to parse a ~60MB XML-file. This file hardly ever changes: on a nightly cron job, It is overwritten with another one.
Are there tricks or ways to keep the parsed file in memory, as a variable, so that I can read from it on incoming requests, but not have to parse it over and over for each incoming request?
Some Pseudocode to illustrate my problem.
get '/projects/:id'
return #nokigiri_object.search("//projects/project[#id=#{params[:id]}]/name/text()")
end
post '/projects/update'
if params[:token] == "s3cr3t"
#nokogiri_object = reparse_the_xml_file
end
end
What I need to know, is how to create such a #nokogiri_object so that it persists when Sinatra runs. Is that possible at all? Or do I need some storage for that?
You could try:
configure do
##nokogiri_object = parse_xml
end
Then ##nokogiri_object will be available in your request methods. It's a class variable rather than an instance variable, but should do what you want.
The proposed solution gives a warning
warning: class variable access from toplevel
You can use a class method to access the class variable and the warning will disappear
require 'sinatra'
class Cache
##count = 0
def self.init()
##count = 0
end
def self.increment()
##count = ##count + 1
end
def self.count()
return ##count
end
end
configure do
Cache::init()
end
get '/' do
if Cache::count() == 0
Cache::increment()
"First time"
else
Cache::increment()
"Another time #{Cache::count()}"
end
end
Two options:
Save the parsed file to a new file and always read that one.
You can save in a file – serialize - a hash with two keys: 'last-modified' and 'data'.
The 'last-modified' value is a date and you check in every request if that day is today. If it is not today then a new file is downloaded, parsed and stored with today's date.
The 'data' value is the parsed file.
That way you parse just once time, sort of a cache.
Save the parsed file to a NoSQL database, for example redis.

Resources