[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

pretty folder tree script

John

4/24/2008 9:02:00 PM

Hola,
My boss asked me to make a nice map of the directories on our web
server for an upcoming meeting. I'm aware of several ways to approach
this, like find and tree, etc, but I'm a Ruby addict, so that's what
I'm using. This gets good, don't bail yet!

What I want to see looks like this:
<div class='folder'>
<div class='title'> Foldername </div>
<span class='file'> file1</span><span class='file'> file2</span>
<div class='folder'>
<div class='title'> Nested Foldername </div>
<span class='file'> file3</span><span class='file'> file4</span>
</div>
</div>

Thus, my nested folders appear nested on the page.

I start by doing a "ls -R > filemap.txt" on the directory I'm
interested in, and then I can process the output file:

# map_file.rb
final_document = File.open('newmap.html', 'w') do |f|
f << "<html><head>\n<style type='text/css'>"
f << "<!-- \n.folder { \n"
f << " background-color: #99CCCC; \nborder: 1px solid #333333;
\ndisplay: block; \nmargin-top: 3px; \nmargin-right: 3px; \nmargin-
left: 6px; \nmargin-bottom: 6px; \n}\n "
f << ".title {\n"
f << " background-color: #D5EAEA;\n width: 99%;\n padding-left: 1%;
\nborder-bottom-width: 1px; \nborder-bottom-style: dotted;\n border-
bottom-color: #333333; \n}\n"
f << ".file {\n"
f << "padding-right: 12px; \n padding-left: 12px;"
f << "\n}\n"
f << "</style></head><body>"
f << "Version 19: <br />\n"
f << "<div class='folder'>\n\t"
File.open('filemap.txt').each do |x|
case x
when /.*:$/
f << "\n<div class='folder'><div class='title'>\n\t"
f << x
f << "\n\t</div>"
when /^\n/
f << "<!-- double-newline: end of folder //--></div>"
else
f << "\n<span class='file'>\n\t"
f << x
f << "\n\t</span>"
end
end
f << "</div>"

f << "\n</body></html>"
end

If you run this, everything goes fine -- except for the nesting part.
I don't think I can solve this one in a case statement - I think I'm
going to need an array like:

arrayname[folder, folder[child]]

and iterate over that, putting my </div> at the end of each nested
array.

I'm just not that good! This one is killing me. Can anyone help?

Mahalo,
John
19 Answers

John

4/25/2008 12:23:00 AM

0

Well, I've gotten this far:

[[".", "Migratus:"], [".", "Migratus", "app:"], [".", "Migratus",
"app", "controllers:"], [".", "Migratus", "app", "helpers:"], [".",
"Migratus", "app", "models:"], [".", "Migratus", "app", "views:"],
[".", "Migratus", "app", "views", "layouts:"], [".", "Migratus",
"app", "views", "projects:"], [".", "Migratus", "app", "views",
"tasks:"], [".", "Migratus", "backup-sunday:"], [".", "Migratus",
"config:"]]

Maybe someone sees how I can transform this into the nested array I
need. If so, your help is appriciated.

Arlen Cuss

4/25/2008 12:48:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Hi,

On Fri, Apr 25, 2008 at 10:25 AM, John <john.d.perkins@gmail.com> wrote:

> Maybe someone sees how I can transform this into the nested array I
> need. If so, your help is appriciated.
>
>
celtic@sohma:~$ cat migratus.rb
def transform_nested data
result = {:name => "root", :elements => []}
data.each do |item|
index = result
item.each do |sub|
sub.gsub! /:$/, ''
new_index = index[:elements].find {|e| e[:name] == sub}
unless new_index
new_index = {:name => sub, :elements => []}
index[:elements] << new_index
end
index = new_index
end
end
result
end


data = [[".", "Migratus:"], [".", "Migratus", "app:"], [".", "Migratus",
"app", "controllers:"], [".", "Migratus", "app", "helpers:"], [".",
"Migratus", "app", "models:"], [".", "Migratus", "app", "views:"],
[".", "Migratus", "app", "views", "layouts:"], [".", "Migratus",
"app", "views", "projects:"], [".", "Migratus", "app", "views",
"tasks:"], [".", "Migratus", "backup-sunday:"], [".", "Migratus",
"config:"]]

require 'pp'
pp transform_nested(data)
celtic@sohma:~$ ruby migratus.rb
{:elements=>
[{:elements=>
[{:elements=>
[{:elements=>
[{:elements=>[], :name=>"controllers"},
{:elements=>[], :name=>"helpers"},
{:elements=>[], :name=>"models"},
{:elements=>
[{:elements=>[], :name=>"layouts"},
{:elements=>[], :name=>"projects"},
{:elements=>[], :name=>"tasks"}],
:name=>"views"}],
:name=>"app"},
{:elements=>[], :name=>"backup-sunday"},
{:elements=>[], :name=>"config"}],
:name=>"Migratus"}],
:name=>"."}],
:name=>"root"}
celtic@sohma:~$

The data may be hard to visualise in that way (I also stripped the ending
`:'s from some items so there were no duplicates), but basically we have the
result item `result':

result[:name] == "root" [the root object]
result[:elements] contains the item with [:name] == ".", which in turn has
[:elements] with the item with name "Migratus", which has "app",
"backup-sunday", "config" - so, nested hashes and arrays, which you can
traverse to your own delight.

HTH!

Arlen

Arlen Cuss

4/25/2008 12:49:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Hi,

Apologies; I should have used the obvious solution and pastebinned it:
http://pastie.caboo...

Cheers,
Arlen.

John

4/25/2008 1:34:00 AM

0

I think you killed a fly with a shotgun this time. I mean it's really
appriciated, but I can't ride this horse.

I was looking for more of a [migratus[app[views, models,
controllers]]... etc] than a hash with empty symbols.

Arlen Cuss

4/25/2008 1:48:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Hi,

On Fri, Apr 25, 2008 at 11:35 AM, John <john.d.perkins@gmail.com> wrote:

> I think you killed a fly with a shotgun this time. I mean it's really
> appriciated, but I can't ride this horse.


Understood, but I think this is probably the simplest data structure you can
get for what you want.


> I was looking for more of a [migratus[app[views, models,
> controllers]]... etc] than a hash with empty symbols.
>

You can't use anything less than arrays *and* something else to do this,
unfortunately - since arrays can only contain other objects [and have no
`name' of their own], we need some other type of object used in conjunction
to actually be the `nodes' of this tree structure. You could do it entirely
with hashes, however, which renders what I think what you want:

result[:migratus][:app][:views], for example. Is this more what you'd be
looking for? My last question, though, is - what do you want at the `leaves'
of this tree? [i.e. what do you get at the very end? - the items under
`views', for example?] Something that produces such nested hashes would just
leave empty hashes (i.e. result[...][:views][:abc] == {}), but that wouldn't
impede your work.

The only thing with using hashes is that you lose any order you might've
had. But perhaps it's not a problem?

http://pastie.caboo...

The result is like this:

{"controllers"=>{},
"views"=>{"projects"=>{}, "tasks"=>{}, "layouts"=>{}},
"helpers"=>{},
"models"=>{}}

Note that we're already "using" the data in some manipulated sense in that
pastie bit by asking for `result["."]["Migratus"]["app"]' specifically.

Hope this helps a bit. If I miss the point still, please let us know!

Arlen

John

4/25/2008 2:01:00 AM

0

hm. Well, here's my code so far:

http://pastie.caboo...

If you run that you'll see how I'm tripped up.

John

4/25/2008 2:49:00 AM

0

I must be clueless - this doesn't work either:

@map.each do |i|
i.each do |k, v|
f << "#{k} is: #{v}<br />"
end
end

Arlen Cuss

4/25/2008 2:52:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Hi,

On Fri, Apr 25, 2008 at 12:50 PM, John <john.d.perkins@gmail.com> wrote:

> I must be clueless - this doesn't work either:
>
> @map.each do |i|
> i.each do |k, v|
> f << "#{k} is: #{v}<br />"
> end
> end
>
>
The way the code is currently set out won't really work at all with it -
I've been working on a replacement for that little loop of yours - I'll post
it as soon as I'm done!

Cheers,
Arlen

Arlen Cuss

4/25/2008 3:01:00 AM

0

[Note: parts of this message were removed to make it a legal post.]

Hi John,

http://pastie.caboo...

Replace the part after the test data with this. It produces the output I'd
expect to see! I added some brief comments so hopefully you can get an idea
of what it's doing.

The next part would probably be sorting, at least at a rudimentary level, so
that we process all the files first, then directories (at one level) - at
the moment, directories and files are interspersed within the same level, so
it's hard to get an overall idea. But perhaps you can work that out!

Cheers,
Arlen

Eivind Eklund

4/25/2008 9:10:00 AM

0

On Thu, Apr 24, 2008 at 11:05 PM, John <john.d.perkins@gmail.com> wrote:
> Hola,
> My boss asked me to make a nice map of the directories on our web
> server for an upcoming meeting. I'm aware of several ways to approach
> this, like find and tree, etc, but I'm a Ruby addict, so that's what
> I'm using. This gets good, don't bail yet!
>
> What I want to see looks like this:
> <div class='folder'>
> <div class='title'> Foldername </div>
> <span class='file'> file1</span><span class='file'> file2</span>
> <div class='folder'>
> <div class='title'> Nested Foldername </div>
> <span class='file'> file3</span><span class='file'> file4</span>
> </div>
> </div>
>
> Thus, my nested folders appear nested on the page.
>
> I start by doing a "ls -R > filemap.txt" on the directory I'm
> interested in, and then I can process the output file:

I think this is your core problem. Don't do that.

Something like this should work:

# Implement this properly
def html_escape(s)
s
end

def recurse_dir(dirname)
Dir.entries(dirname).sort.each do |filename|
if FileTest.directory? filename
puts "<div class=\"folder\">"
puts "<div class=\"title\">#{html_escape(filename)}</div>"
recurse_dir(filename)
puts "</div>"
elsif FileTest.file? filename
puts "<span class=\"file\">#{html_escape(filename)}</span>"
end
end
end

recurse_dir("/wherever/you/start")


Eivind.