Ruby Sinatra: show clickable list of files - ruby

i have this simple script to show all files in a folder, it works in the console but gives a different result in Sinatra (with path and extension). Why is this so, and how can i best present these basenames (without path and extension) in a ul list as a link to open this file in the browser using Sinatra ?
The goal is to present a clickable list of pages to open if no filename is given. I allready have the routine to show the files.
console:
require 'find'
def get_files path
dir_array = Array.new
Find.find(path) do |f|
dir_array << f if !File.directory?(f) # add only non-directories
end
return dir_array
end
for filename in get_files 'c:/sinatra_wiki/views'
basename = File.basename(filename, ".*")
puts basename
end
=> index
index2
Sinatra:
require 'find'
def get_files path
dir_array = Array.new
Find.find(path) do |f|
dir_array << f if !File.directory?(f) # add only non-directories
end
return dir_array
end
get '/' do
for filename in get_files 'c:/sinatra_wiki/views'
basename = File.basename(filename, ".*")
puts basename
end
end
=> c:/sinatra_wiki/views/index.htmlc:/sinatra_wiki/views/index2.erb

In your sinatra implementation, the result you see in the browser is not the one from the puts basename statement in the get block. It's the return value of the get_files method. Try adding puts "<p>#{base name}</p>" instead of the puts basename in the get block and see for yourself.
Some changes:
The get_files method: Instead of sending the entire file path, send only the file name
dir_array << File.basename(f, ".*")
Add a view in case you need clarity:
get '/' do
#arr = get_files(the_path)
erb :index
end
elsewhere, in the app/views folder, in an index.erb file:
<h2>Page list</h2>
<ul>
<% #arr.each do |page| %>
<li><%=page> %></li>
<% end %>
</ul>
This is to list out the file names in a similar way to that of the console output.
TL;DR: Put the looping part in the view!

Related

RubyZip docx issues with write_buffer instead of open

I'm adapting the RubyZip recursive zipping example (found here) to work with write_buffer instead of open and am coming across a host of issues. I'm doing this because the zip archive I'm producing has word documents in it and I'm getting errors on opening those word documents. Therefore, I'm trying the work-around that RubyZip suggests, which is using write_buffer instead of open (example found here).
The problem is, I'm getting errors because I'm using an absolute path, but I'm not sure how to get around that. I'm getting the error "#//', name must not start with />"
Second, I'm not sure what to do to mitigate the issue with word documents. When I used my original code, which worked and created an actual zip file, any word document in that zip file had the following error upon opening: "Word found unreadable content in Do you want to recover the contents of this document? If you trust the source of this document, click Yes." The unreadable content error is the reason why I went down the road of attempting to use write_buffer.
Any help would be appreciated.
Here is the code that I'm currently using:
require 'zip'
require 'zip/zipfilesystem'
module AdvisoryBoard
class ZipService
def initialize(input_dir, output_file)
#input_dir = input_dir
#output_file = output_file
end
# Zip the input directory.
def write
entries = Dir.entries(#input_dir) - %w[. ..]
path = ""
buffer = Zip::ZipOutputStream.write_buffer do |zipfile|
entries.each do |e|
zipfile_path = path == '' ? e : File.join(path, e)
disk_file_path = File.join(#input_dir, zipfile_path)
#file = nil
#data = nil
if !File.directory?(disk_file_path)
#file = File.open(disk_file_path, "r+b")
#data = #file.read
unless [#output_file, #input_dir].include?(e)
zipfile.put_next_entry(e)
zipfile.write #data
end
#file.close
end
end
zipfile.put_next_entry(#output_file)
zipfile.put_next_entry(#input_dir)
end
File.open(#output_file, "wb") { |f| f.write(buffer.string) }
end
end
end
I was able to get word documents to open without any warnings or corruption! Here's what I ended up doing:
require 'nokogiri'
require 'zip'
require 'zip/zipfilesystem'
class ZipService
# Initialize with the directory to zip and the location of the output archive.
def initialize(input_dir, output_file)
#input_dir = input_dir
#output_file = output_file
end
# Zip the input directory.
def write
entries = Dir.entries(#input_dir) - %w[. ..]
::Zip::File.open(#output_file, ::Zip::File::CREATE) do |zipfile|
write_entries entries, '', zipfile
end
end
private
# A helper method to make the recursion work.
def write_entries(entries, path, zipfile)
entries.each do |e|
zipfile_path = path == '' ? e : File.join(path, e)
disk_file_path = File.join(#input_dir, zipfile_path)
if File.directory? disk_file_path
recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
else
put_into_archive(disk_file_path, zipfile, zipfile_path, e)
end
end
end
def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
zipfile.mkdir zipfile_path
subdir = Dir.entries(disk_file_path) - %w[. ..]
write_entries subdir, zipfile_path, zipfile
end
def put_into_archive(disk_file_path, zipfile, zipfile_path, entry)
if File.extname(zipfile_path) == ".docx"
Zip::File.open(disk_file_path) do |zip|
doc = zip.read("word/document.xml")
xml = Nokogiri::XML.parse(doc)
zip.get_output_stream("word/document.xml") {|f| f.write(xml.to_s)}
end
zipfile.add(zipfile_path, disk_file_path)
else
zipfile.add(zipfile_path, disk_file_path)
end
end
end

display page in iframe

post '/uploadZIP' do
zipFile = params['zip'][:filename]
if zipFile.end_with? '.zip'
File.open(params['zip'][:filename], 'w') do |f|
f.write(params['zip'][:tempfile].read)
end
extract_zip(zipFile, '..\VotersBest\public')
$websites = get_websites()
redirect to('/loginTA')
end
end
#STORE WEBSITES IN ARRAY
def get_websites()
$directory = "public"
if Dir.exist? $directory
siteArray = Array.new
# go to pwd/dir
Dir.chdir($directory) { siteArray = Dir.glob("*").select {|x| Dir.exist? x }}
# go back to parent dir
siteArray
end
end
get '/websites' do
#instance_websites = $websites
#website_url = $directory+ "/"+ #instance_websites[0] + "/index.html"
erb :websites
end
it says sinatra doesnt know this ditty and i know thas because i dont have a get request but is there anyway to get around that? I have websites in different folders and I dont want to make a get request for each folder path
the line
#website_url = $directory+ "/"+ #instance_websites[0] + "/index.html"
gets the error
it says to try
get '/public/student1/index.html' do
"Hello World"
end
but there could be time where the folder isnt called "student"

How to take the result from another method

I have a directory structure with sub-directories:
../../../../../MY_PROJECT/TEST_A/cats/
../../../../../MY_PROJECT/TEST_B/dogs/
../../../../../MY_PROJECT/TEST_A/tigers/
../../../../../MY_PROJECT/TEST_A/elephants/
each of which has a file that ends with ".sln":
../../../../../MY_PROJECT/TEST_A/cats/cats.sln
../../../../../MY_PROJECT/TEST_B/dogs/dogs.sln
...
These files contain information specific to their directory. I would like to do the following:
Create a file "myfile.txt" within each sub-directory, and write some strings to them:
../../../../../MY_PROJECT/TEST_A/cats/myfile.txt
../../../../../MY_PROJECT/TEST_B/dogs/myfile.txt
../../../../../MY_PROJECT/TEST_A/tigers/myfile.txt
../../../../../MY_PROJECT/TEST_A/elephants/myfile.txt
Copy a specific string in the ".sln" files to the myfile.txt of certain directories using the following method:
def parse_sln_files
sln_files = Dir["../../../../../MY_PROJECT/TEST_*/**/*.sln"]
sln_files.each do |file_name|
File.open(file_name) do |f|
f.each_line { |line|
if line =~ /C Source files ="..\\/ #"
path = line.scan(/".*.c"/)
puts path
end
}
end
end
end
I would like to do something like this:
def create_myfile
Dir['../../../../../MY_PROJECT/TEST_*/*/'].each do |dir|
File.new File.join(dir, 'myfile.txt'), 'w+'
Dir['../../../../../TEST/TEST_*/*/myfile.txt'].each do |path|
File.open(path,'w+') do |f|
f.puts "some text...."
f.puts "some text..."
f.puts # here I would like to return the result of parse_sln_files
end
end
end
end
Any suggestions on how to express this?
It seems like you want to read list of C file names from a Visual C++ Solution file, and store in a separate file in the same directory. You may have to merge the two loops that you have shown in your code, and do something like this:
def parse_sln_and_store_source_files
sln_files = Dir["../../../../../MY_PROJECT/TEST_*/**/*.sln"]
sln_files.each do |file_name|
#### Lets collect source file names in this array
source_file_names = []
File.open(file_name) do |f|
f.each_line { |line|
if line =~ /C Source files ="..\\/ #"
path = line.scan(/".*.c"/)
############ Add path to array ############
source_file_names << path
end
}
end
#### lets create `myfile.txt` in same dir as that of .sln
test_file = File.expand_path(File.dirname(file_name)) + "/myfile.txt"
File.open(test_file,'w+') do |f|
f.puts "some text...."
f.puts "some text..."
##### Iterate over source file names & write to file
source_file_names.each { |n| f.puts n }
end
end
end
This can be done bit more elegantly with few more refactoring. Also note that this is not tested code, hopefully, you get the gist of what I am suggesting.

How to get Ruby Dir#glob to return basenames, not absolute_paths?

FakeProfilePictures::Photo.all_large_names_2x (defined below) returns an array of absolute path names, but when I do Dir["picture_*#2x.*"] from the correct directory in irb, I only get the basenames (what I want). What's the best way to get the base names? I know I could do it by adding .map { |f| File.basename(f) } as shown in the comment, but is there an easier/better/faster/stronger way?
module FakeProfilePictures
class Photo
DIR = File.expand_path(File.join(File.dirname(__FILE__), "photos"))
# ...
def self.all_large_names_2x
##all_large_names_2x ||= Dir[File.join(DIR, "picture_*#2x.*")] # .map { |f| File.basename(f) }
end
end
end
You can do
Dir.chdir(DIR) do
Dir["picture_*#2x.*"]
end
after the block, the original dir is restored.
You could chdir into DIR before globbing, but I would just run everything through basename.

How can I create a program to create multiple files from one template with different values inside?

I need to create 1500+ ruby files from a template for some testing I'm doing with Selenium.
The template looks like this:
class $CLASS_NAME
require "spec"
attr_accessor :title
include Spec::Example::ExampleGroupMethods
include Spec::Matchers
def initialize
#title = "$OLD_URL -> $NEW_URL"
end
def execute(selenium)
selenium.open "$OLD_URL"
sleep 1
puts 'Opening...'
sleep 1
url = selenium.get_location
puts 'Grabbing location...'
sleep 1
puts 'The URL is ' + url
puts 'Doing match...'
sleep 1
/$NEW_URL/.match(url).should_not be nil
puts "\n##### Success! #####\n\r"
end # execute
I have a load of URL's I need to insert - one into each file, replacing '$OLD_URL' and '$NEW_URL'.
Is there anyway to do something like this?
x = 0
while (x < 1500)
{
open template.rb
find all instances of $CLASS_NAME and replace with xxx from classnames.txt
find all instances of $OLD_URL and replace with xxx from listofurls.csv
find all instances of $NEW_URL and replace with xxx from listofurls.csv
save file as ('redirect_' + 'x++')
x++
}
The proper way to do it is using the ERB library.
The code below will generate two files according to predefined template.
require "erb"
File.open("template.erb") do |io|
template = ERB.new io.read
files = {:anecdote => "There are 10 types of people in the world.",
:story => "Once upon a time..."}
files.each do |file, contents|
File.open "#{file}.txt", "w" do |out|
out.puts template.result binding
end
end
end
When template.erb looks like:
Title <%= file %>
<%= "=" * 40 %>
<%= contents %>
<%= "=" * 40 %>
Here is the contents of aneqdote.txt:
Title anecdote
========================================
There are 10 types of people in the world.
========================================
Read the contents of template.rb to a string, then use String#gsub to edit the template inside the loop and save the modified template.

Resources