How to make x-callback-url call to local app in Ruby? - ruby

I have a local app (NotePlan) installed on macOS, and it publishes an x-callback-url scheme. I'm simply trying to call it from a ruby script, but can't find help on this. (Plenty of help available for HTTP calls, but this is to a local app.)
require 'open-uri'
title = "note title"
uri = "noteplan://x-callback-url/openNote?noteTitle=#{title}"
uriEncoded = URI.escape(uri)
response = open(uriEncoded).read
It returns this error:
No such file or directory # rb_sysopen - noteplan://x-callback-url/openNote?noteTitle=note%20title (Errno::ENOENT)
From the command line calling open "noteplan://x-callback-url/openNote?noteTitle=note%20title" does the expected thing, so the basic mechanism appears to work.

Well, I've found a way, though I regard it a hack, not a good answer.
response = %x[open "#{uri}"]
i.e. call the open command in a new shell.
I still hope someone can provide a better answer.

Related

Dir.chdir(File.dirname(__FILE__)) throws Errno::ENOENT

I got a method where I use Dir.chdir(File.dirname(__FILE__)) inside. I am using it so that I can run the ruby file from anywhere and not get this error: No such file or directory # rb_sysopen - 'filename' (Errno::ENOENT).
Using the method for the first time works fine but using it for the second time throws an error. See method and exact error below.
def meth(string)
Dir.chdir(File.dirname(__FILE__))
hash = JSON.parse(File.read("file.json"))
# do something with hash and string
# return some value
end
meth("first string") # this returns what is expected
meth("second string") # this second usage of the method throws the error
Error sample pinpointing the line where I used Dir.chdir(File.dirname(__FILE__)):
dir/decoder.rb:44:in `chdir': No such file or directory # dir_s_chdir - lib (Errno::ENOENT)
Not sure if OS plays a role here, I am using an m1 BigSur on version 11.2.3.
Why is this happening?
What needs to be done so that the method` can be used as much as needed without running into the error?
Your problem here seems to be that __FILE__ is a relative path like dir/decoder.rb and that path becomes invalid after the first time Dir.chdir is used, because that command changes the working directory of your entire Ruby process. I think the solution would be to do something like this in your decoder.rb file:
DecoderDir = File.realpath(File.dirname(__FILE__))
def meth
Dir.chdir(DecoderDir)
# ...
end
I'm guessing that the first time the Ruby interpreter processes the file, that is early enough that the relative path in __FILE__ still refers to the right place. So, at that time, we generate an absolute path for future use.
By the way, a well-behaved library should not run Dir.chdir because it will affect all use of relative paths throughout your entire Ruby process. I pretty much only run Dir.chdir once and I run it near the beginning of my top-level script. If you're making a reusable library, you might want to do something like this to calculate the absolute path of the file you want to open:
DecoderJson = File.join(DecoderDir, 'file.json')

Open a local file with open-uri

I am doing data scraping with Ruby and Nokogiri. Is it possible to download and parse a local file in my computer?
I have:
require 'open-uri'
url = "file:///home/nav/Desktop/Scraping/scrap1.html"
It gives error as:
No such file or directory # rb_sysopen - file:\home/nav/Desktop/Scraping/scrap1.html
If you want to parse a local file with Nokogiri you can do it like this.
file = File.read('/home/nav/Desktop/Scraping/scrap1.html')
doc = Nokogiri::HTML(file)
When you open a local file in a browser, the URL in the address bar is displayed as:
file:///Users/7stud/Desktop/accounts.txt
But that doesn't mean you use that format in a Ruby script. Your Ruby script doesn't send the file name to a browser and then ask the browser to retrieve the file. Your Ruby script searches your file system directly.
The same is true for URLs: your Ruby script doesn't ask your browser to go retrieve a page from the internet, Ruby retrieves the page itself by sending a request using your system's network interface. After all, a browser and a Ruby program are both just computer programs. What your browser can do over a network, a Ruby program can do, too.
This works for me:
require 'open-uri'
text = open('./data.txt').read
puts text
You have to get your path right, though. The only reason I can think of to use open() is if you had an array of filenames and URLs mixed together. If that isn't your situation, see new2code's answer.
This is how I do it as according to the documentation.
f = File.open("//home/nav/Desktop/Scraping/scrap1.html")
doc = Nokogiri::HTML(f)
f.close
I would make use of Mechanize and save the file locally, then parse it with Nokogiri like so:
# Save the file
agent = Mechanize.new
agent.pluggable_parser.default = Mechanize::Download
current_url = 'http://www.example.com'
file = agent.get(current_url)
file.save!("#{Rails.root}/tmp/")
# Read the file
page = Nokogiri::HTML::Reader(File.open(file))
Hope that helps!

Chef cookbook - copy file to local user with '~' ruby variable

Well, let's start by saying I'm a chef noob and I am trying to hash this code out.
I am in a full mac shop. I am using Chef to automate system wide changes. As I'm new, I'm rolling it out onto our Mac AV systems.
Basically, there is a folder on a file server that has MAC SCREEN SAVERS directory. I copy the server directory locally to the MAC OS X /User/user_name/Pictures directory.
So, this is what I got in chef:
local_folder_modified = File.mtime("~/Pictures/SCREEN SAVER NEW MACS")
server_folder_modified = File.mtime("/Volumes/SERVER/SCREEN\ SAVER\ NEW\ MACS/")
if server_folder_modified != local_folder_modified
# file has changed
then
require 'fileutils'
FileUtils.cd('server_folder_modified') do
FileUtils.rm('local_folder_modified/*')
FileUtils.cp_r './*', 'local_folder_modified'
Else
end
end
Anyways, I can't figure how to set the '~' to be the running user of this recipe. So, if Comp_A has user Jim_Beam and Comp_B has user Jack_Daniels, I don't want to set the code to be:
ENV[HOME] = /user/jimbeam
As it won't work on Jack_Daniels. Right?
I've read that file.expand will work, or ENV, but I am really unsure what will be the best code to say
"hey, I want the current user that will need this screen saver - so set the environment as a variable so it works across different nodes".
Anyways, thanks for your help. I hope I am making sense!
Yes, use File.expand. It will expand the tilde ~ to be the the home directory of the user running this cookbook. Alternatively, you could do:
"#{ENV['HOME']}/Pictures/SCREEN SAVER NEW MACS"
Like the previous comment, this is not chef DSL or ruby code. What is the source of this code or is it just pseudo-code to ask the question?
Also, chef-client is not frequently run as multiple users in a chef server deployment. It's usually run in a sudo context. So maybe you are referring to a --local-mode or chef-zero application?
You may want to use file stat of /dev/console to get the current user. Depending how you are running the chef-client Env[‘Home’] might not give you want you want. Try this:
console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
home_dir = ::File.join(‘Users’, console_user)
You can see that the chef launchd provider uses this method to determine the console user
Also there is a much simpler way to do what you are trying to accomplish with the remote_file resource. Try this:
console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
home_dir = ::File.join(‘Users’, console_user)
pics = ::File.join("#{home_dir}/Pictures/")
server_base_url = "https://PLACE_WHERE_STORE/Wallpapers")
[
‘Pic1’,
‘Pic2’,
].each do |pic|
remote_file ::File.join(pics, pic) do
source “#{server_base_url}/#{pic}”
owner console_user
group console_user
mode '0755'
action :create
end
end
For added security you should also include checksum

passing arguaments to a system call in Rails, but not plain ruby program

Ubuntu 12.04
Sinatra 1.3.3
Why does passing an argument to a ruby system call (%x[] or ``) give me a 'not found' error in my sinatra app? The same code works fine in a normal ruby script running from the same directory.
I have a file test.rb like this
output = %x["ls"]
p output
When I run it with "ruby test.rb" I get the contents of the current directory in the console, as expected.
If I modify the program to give an argument to the system call like so:
output = %x["ls sub_dir/"]
p output
I get the contents of sub_dir, which sits in the current directory, as expected.
So far so good.
Now if I make a Sintra app with a post method:
require 'rubygems'
require 'bundler/setup'
require 'sinatra'
post "/" do
output = x["ls"]
return output
end
The response to a Post call to "/" returns the contents of the current directory, which includes 'sub_dir', as expected.
If I try to add the argument to the system call to the sinatra app like so:
require 'rubygems'
require 'bundler/setup'
require 'sinatra'
post "/" do
output = x["ls sub_dir/"]
return output
end
the response is nil and there is an error in the console:
sh: 1: ls sub_dir/: not found
Why does adding a parameter to a system call in my sinatra app cause it to crash, when the same code called from a plain ruby script, run from the same location works perfectly.
By the way, the 'ls' example shown here is not the command I really need to run, so please don't explain a different way to get this information. I have an executable file that takes a file name as a parameter that I need to run, which behaves exactly the same way.
Thanks in advance!
If you want to specify a path in relation to the application, you could use something like this:
post "/" do
path = File.join(File.dirname(__FILE__), "sub_dir")
%x[ls #{path}]
end
However, if you want to list the contents of a directory, why not do it in Ruby?
I rewrote the sinatra app in another file in the same directory.
Everything works as expected.
I did not find the reason and I deleted the original file so that I won't lose anymore time trying to figure it out.

Using yaml files within gems

I'm just working on my first gem (pretty new to ruby as well), entire code so far is here;
https://github.com/mikeyhogarth/tablecloth
One thing I've tried to do is to create a yaml file which the gem can access as a lookup (under lib/tablecloth/yaml/qty.yaml). This all works great and the unit tests all pass, hwoever when I build and install the gem and try to run under irb (from my home folder) I am getting;
Errno::ENOENT: No such file or directory - lib/tablecloth/yaml/qty.yaml
The code is now looking for the file in ~/lib/tablecloth... rather than in the directory the gem is installed to. So my questions are;
1) How should i change line 27 of recipe.rb such that it is looking in the folder that the gem is installed to?
2) Am I in fact approaching this whole thing incorrectly (is it even appropriate to use static yaml files within gems in this way)?
Well first of all you should refer to the File in the following way:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
units_hash = YAML.load_file(filepath)
File.dirname(__FILE__) gives you the directory in which the current file (recipe.rb) lies.
File.join connects filepaths in the right way. So you should use this to reference the yaml-file relative to the recipe.rb folder.
If using a YAML-file in this case is a good idea, is something which is widely discussed. I, myself think, this is an adequate way, especially in the beginning of developing with ruby.
A valid alternative to yaml-files would be a rb-File (Ruby Code), in which you declare constants which contain your data. Later on you can use them directly. This way only the ruby-interpreter has to work and you save computing time for other things. (no parser needed)
However in the normal scenario you should also take care that reading in a YAML file might fail. So you should be able to handle that:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
begin
units_hash = YAML.load_file(filepath)
rescue Psych::SyntaxError
$stderr.puts "Invalid yaml-file found, at #{file_path}"
exit 1
rescue Errno::EACCES
$stderr.puts "Couldn't access file due to permissions at #{file_path}"
exit 1
rescue Errno::ENOENT
$stderr.puts "Couldn't access non-existent file #{file_path}"
exit 1
end
Or if you don't care about the details:
file_path = File.join(File.dirname(__FILE__),"yaml/qty.yaml")
units_hash =
begin
YAML.load_file(filepath)
rescue Psych::SyntaxError, Errno::EACCES, Errno::ENOENT
{}
end

Resources