How to recursively read files and folders in ruby - ruby-on-rails

folks.
I've been struggling with a deceptively tricky problem for a few days now. I had been determined to crack it on my own but alas, here I am.
The aim is to create a menu system during the installation of a Rails 5 application using seeds.rb. The menu should mirror an existing directory structure. The code is supposed to start at /home/install/sitename/ and from there, every folder it encounters should become a menu category and every file should become a menu item ( == post).
So far, I have been able to get the folder structure right using the code below, but I can't get it to make the menu items properly.
this_item = 0
Dir.chdir(#installation_media_path + '/menu/') do
this_level = Dir.glob('*/').sort
this_item += 1
# for each folder at this level
this_level.each do |item|
# remove the trailing slash
item = item[0...-1]
# take the number from the beginning of the filename and use it for the menu position
position = item.split(' ')[0]
# the remainder of the filename is used as the item title
title = item.sub(position.to_s + ' ', '')
puts item.to_s + '...'
# create the menu item
brief = File.read(#installation_media_path + '/menu/' + item + '/.brief') if File.exist?(#installation_media_path + '/menu/' + item + '/.brief')
PostCategory.create!(
title: title,
parent: 1,
position: position
)
this_item = PostCategory.last.id
There can be up to four levels of nested directories, and for each one, I repeat this block and use this_item as the parent. I want to do this without repeating code but I can't figure out how to do it.
And then somewhere in there, I need to incorporate the following code, but so far, my attempts have resulted in repeated or blank entries, because I can't figure out where to put this block.
if File.exist?(#installation_media_path + '/menu/' + item + '/*.txt')
articles = Dir.glob('*.txt').sort
articles.each do |article|
title = File.basename(article, ".txt")
body = File.read(article)
Post.create!(title: title, meta_desc: "",
user_id: 1,
post_category_id: this_item,
body: body
)
end
end
Anyway, you get the idea. If anyone can help me figure this out, I'd be very grateful.

Related

Problem to save render jpeg with maxscript

I am making a script to automate the process of render multiple .max files. I almost finished what I pretend to achive, the only problem is that I don't know how to save the render image by maxscript. I tried several ways I foud on the internet but none of them works.
studioFile = getOpenFileName caption: "Select the Studio"
loadMaxFile studioFile
folderPath = getSavePath caption: "Select the Folder with the Assets to Render"
maxPath = folderPath + "\*.max"
maxFiles = getFiles maxPath
renderPath = getSavePath caption: "Select the Render Folder"
for current in maxFiles do(
xrefs.addNewXRefFile current
currentName = getFilenameFile current
print currentName
for c in cameras where classof c != Targetobject do(
render camera:c output: ("E:\\MUVA\\Renders\\" + currentName + "_" + c.name + "_" + ".jpeg")
)
xrefs.deleteAllXRefs()
)
This is how my code is for now and explaining it:
First, I made a Dialog Box for the user to select what I am calling "Studio" that is a scene with lighting and cameras ready for the render and then open it;
Second, is another Dialog Box for the user to select the folder where the .max files to render are;
Thrid, is another Dialog Box for the user to select the folder where he wish to save the renders;
Then I made a loop where through a list, the program will add the .max file to render as a xref scene and rigth after that get the name of the .max file to use in the saving.
The next and final loop is to get a render from each camera in the scene and then save but the problem is that the image is not been saved in the folder selected.
I really don't know more what to do. So, I would be very grateful if somebody could help me with this.
PS.: The selected folder to save the renders is not been used in the output of the render by now because I was testing putting all the path to the folder.
As per MAXScript reference, the parameter for filename is outputfile:. In your case the line would be:
render camera:c outputfile:("E:\\MUVA\\Renders\\" + currentName + "_" + c.name + "_" + ".jpeg")
There's also another way: you can save the bitmap object that the render() function returns:
bm = render camera:c
bm.filename = "E:\\MUVA\\Renders\\" + currentName + "_" + c.name + "_" + ".jpeg"
save bm
The directory must exist for any of these methods to work, so you may want to create it before your loop:
makeDir "E:\\MUVA\\Renders" all:true

Call the name of a Texbox with number changing in Visual Basic 6

I'm use Visual Basic 6 to create a table with many Textbox which named txtNo1, txtNo2, txtNo3,...
I want to use the "For...Next..." loop to assign a content to these Textbox.
How can I call all these Textbox in the simplest way?
For i = 1 to 100
txtNo (......) .txt = "ABC"
Next i
Instead of using unique textboxes, each with a unique name, you should use a (textbox) control array:
Place the 1st textbox on the form, name it 'txtNo'
Copy it and paste it onto the form
VB will ask you "There's already a control named 'txtNo'. Would you like to create a control array?". Answer "Yes"
Paste as the textbox as many times as you need it
Then your code looks like
' Control arrays typically start at index 0
For i = 0 to 100
txtNo(i) .txt = "ABC"
Next i
Jim Mack's solution works as well, code for it:
' Assuming your form is named 'Form1'
For each ctrl in Form1.Controls
If TypeOf ctrl Is Textbox
For i = 1 To 100
If ctrl.Name = "txtNo" & CStr(i) Then
ctrl.Text = "ABC"
End If
End If
End If
It's a bit more complex, but therefore more flexible as works with multiple control types (in one loop).
If you need an easiest way to create your textboxes as a table, you can Load the controls at runtime. You have to add only the first TextBox control to your form, set the name to "txtNo", and Index to 0 in the Properties window.
In your code, call Load() to create additional controls, and you can set the Top/Left and other properties
For i = 1 To 100
Load txtNo(i)
txtNo(i).Top = txtNo(i - 1).Top + txtNo(i - 1).Height + 150
txtNo(i).Left = txtNo(i - 1).Left
txtNo(i).Text = "Textbox " & i
txtNo(i).Visible = True
Next i
If you need again to change any control property, from your list of controls, you can iterate only over your control list, instead of all controls of your Form
For i = txtNo.LBound() To txtNo.UBound()
Form1.Controls("txtNo")(i).Text = "New text " & i
Next i

undefined method `click' for "2":String, Rails error when using Mechanize

class ScraperController < ApplicationController
def show
mechanize = Mechanize.new
website = mechanize.get('https://website.com/')
$max = 2
$counter = 0
$link_to_click = 2
#names = []
while $counter <= $max do
#names.push(website.css('.memName').text.strip)
website.link_with(:text => '2').text.strip.click
$link_to_click += 1
$counter += 1
end
end
end
I am trying to scrape 20 items off of each page and then click on the link at the bottom (1, 2, 3, 4, 5, etc.). However, I get the error as seen in the title which tells me that I cannot click the string. So it recognizes that the button '2' exists but will tell me if cannot click it. Ideally, once this is sorted out, I wanted to the use the $link_to_click variable as a way to replace the '2' so that it will increment each time but it always comes back as nil. I have also changed it to .to_s with the same result.
If I remove the click all together, it will scrape the same page 3 times instead of moving onto the next page. I have also removed the text.strip part before the .click and it will do the same thing. I have tried many variations but have had no luck.
I would really appreciate any advice you could offer.
I ended up reviewing the articles I was referencing to solve this and came to this conclusion.
I changed the website_link to website = website.link_with(:text => $link_to_click.to_s).click (because it only worked as a string) and it printed out the first page, second and each one thereafter.
These are the articles that I was referencing to learn how to do this.
http://docs.seattlerb.org/mechanize/GUIDE_rdoc.html
and
https://readysteadycode.com/howto-scrape-websites-with-ruby-and-mechanize

Sphinx references to other sections containing section number and section title

I am using Sphinx to write a document with lots of references:
.. _human-factor:
The Human Factor
================
...
(see :ref:`human-factor` for details)
The compiled document contains something like this:
(see The Human Factor for details)
Instead I would like to have it formatted like this:
(see 5.1 The Human Factor for details)
I tried to google the solution and I found out that the latex hyperref package can do this but I have no idea how to add this to the Sphinx build.
I resolved it by basically using numsec.py from here: https://github.com/jterrace/sphinxtr
I had to replace the doctree_resolved function with this one to get section number + title (e.g. "5.1 The Human Factor").
def doctree_resolved(app, doctree, docname):
secnums = app.builder.env.toc_secnumbers
for node in doctree.traverse(nodes.reference):
if 'refdocname' in node:
refdocname = node['refdocname']
if refdocname in secnums:
secnum = secnums[refdocname]
emphnode = node.children[0]
textnode = emphnode.children[0]
toclist = app.builder.env.tocs[refdocname]
anchorname = None
for refnode in toclist.traverse(nodes.reference):
if refnode.astext() == textnode.astext():
anchorname = refnode['anchorname']
if anchorname is None:
continue
linktext = '.'.join(map(str, secnum[anchorname]))
node.replace(emphnode, nodes.Text(linktext
+ ' ' + textnode))
To make it work one needs to include the numsec extension in conf.py and also to add :numbered: in the toctree like so:
.. toctree::
:maxdepth: 1
:numbered:

Truncate Markdown?

I have a Rails site, where the content is written in markdown. I wish to display a snippet of each, with a "Read more.." link.
How do I go about this? Simple truncating the raw text will not work, for example..
>> "This is an [example](http://example.com)"[0..25]
=> "This is an [example](http:"
Ideally I want to allow the author to (optionally) insert a marker to specify what to use as the "snippet", if not it would take 250 words, and append "..." - for example..
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
The marker could be thought of like an EOF marker (which can be ignored when displaying the full document)
I am using maruku for the Markdown processing (RedCloth is very biased towards Textile, BlueCloth is extremely buggy, and I wanted a native-Ruby parser which ruled out peg-markdown and RDiscount)
Alternatively (since the Markdown is translated to HTML anyway) truncating the HTML correctly would be an option - although it would be preferable to not markdown() the entire document, just to get the first few lines.
So, the options I can think of are (in order of preference)..
Add a "truncate" option to the maruku parser, which will only parse the first x words, or till the "excerpt" marker.
Write/find a parser-agnostic Markdown truncate'r
Write/find an intelligent HTML truncating function
Write/find an intelligent HTML truncating function
The following from http://mikeburnscoder.wordpress.com/2006/11/11/truncating-html-in-ruby/, with some modifications will correctly truncate HTML, and easily allow appending a string before the closing tags.
>> puts "<p><b>Something</p>".truncate_html(5, at_end = "...")
=> <p><b>Someth...</b></p>
The modified code:
require 'rexml/parsers/pullparser'
class String
def truncate_html(len = 30, at_end = nil)
p = REXML::Parsers::PullParser.new(self)
tags = []
new_len = len
results = ''
while p.has_next? && new_len > 0
p_e = p.pull
case p_e.event_type
when :start_element
tags.push p_e[0]
results << "<#{tags.last}#{attrs_to_s(p_e[1])}>"
when :end_element
results << "</#{tags.pop}>"
when :text
results << p_e[0][0..new_len]
new_len -= p_e[0].length
else
results << "<!-- #{p_e.inspect} -->"
end
end
if at_end
results << "..."
end
tags.reverse.each do |tag|
results << "</#{tag}>"
end
results
end
private
def attrs_to_s(attrs)
if attrs.empty?
''
else
' ' + attrs.to_a.map { |attr| %{#{attr[0]}="#{attr[1]}"} }.join(' ')
end
end
end
Here's a solution that works for me with Textile.
Convert it to HTML
Truncate it.
Remove any HTML tags that got cut in half with
html_string.gsub(/<[^>]*$/, "")
Then, uses Hpricot to clean it up and close unclosed tags
html_string = Hpricot( html_string ).to_s
I do this in a helper, and with caching there's no performance issue.
You could use a regular expression to find a line consisting of nothing but "^" characters:
markdown_string = <<-eos
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
eos
preview = markdown_string[0...(markdown_string =~ /^\^+$/)]
puts preview
Rather than trying to truncate the text, why not have 2 input boxes, one for the "opening blurb" and one for the main "guts". That way your authors will know exactly what is being show when without having to rely on some sort of funkly EOF marker.
I will have to agree with the "two inputs" approach, and the content writer would need not to worry, since you can modify the background logic to mix the two inputs in one when showing the full content.
full_content = input1 + input2 // perhaps with some complementary html, for a better formatting
Not sure if it applies to this case, but adding the solution below for the sake of completeness. You can use strip_tags method if you are truncating Markdown-rendered contents:
truncate(strip_tags(markdown(article.contents)), length: 50)
Sourced from:
http://devblog.boonecommunitynetwork.com/rails-and-markdown/
A simpler option that just works:
truncate(markdown(item.description), length: 100, escape: false)

Resources