Automagically adding yard doc skeletons to existing Rails legacy code [closed] - ruby

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
I would like be able to insert templated YARD doc style comments into my existing Rails legacy application. At present it has few comments. I would like class headers and method headers that have the params specified (by extraction from the method signatures I presume) and placeholders for return values.
In PHP code I had tools that would examine the code and create the doc header comments inserted into the code in the proper spots. In Ruby with Duck typing etc, I am certain that things like the types of the #params etc, cannot be easily guessed at, and I am ok with that - I expect to review the code files one by one manually after insertion. Would just like to avoid having to insert all the skeleton templates into the code (over 500 files) if possible.
I have searched for a gem, etc. that does this and have come across none so far. Are there any out there?

It seems you will have to write it by yourself, but this is not a big problem havig access to the Ruby's S-expressions, which will parse the source for you. So you can do it like this:
require 'ripper'
def parse_sexp( sexp, stack=[] )
case sexp[0]
when :module
name = sexp[1][1][1]
line_number = sexp[1][1][2][0]
parse_sexp(sexp[2], stack+[sexp[0], sexp[1][1][1]])
puts "#{line_number}: Module: #{name}\n"
when :class
name = sexp[1][1][1]
line_number = sexp[1][1][2][0]
parse_sexp(sexp[3], stack+[sexp[0], sexp[1][1][1]])
puts "#{line_number}: Class: #{stack.last}::#{name}\n"
when :def
name = sexp[1][1]
line_number = sexp[1][2][0]
parse_sexp(sexp[3], stack+[sexp[0], sexp[1][1]])
puts "#{line_number}: Method: #{stack.last}##{name}\n"
else
if sexp.kind_of?(Array)
sexp.each { |s| parse_sexp(s,stack) if s.kind_of?(Array) }
end
end
end
sexp = Ripper.sexp(open 'prog.rb')
parse_sexp(sexp)
Prog.rb was:
$ cat -n prog.rb
1 module M1
2 class C1
3 def m1c1
4 a="test"
5 puts "hello"
6 return a if a.empty?
7 puts "hello2"
8 a
9 end
10 end
11 class C2 < C3
12 def m1c2
13 puts "hello"
14 end
15 end
16 class C3
17 end
18 end
What you'll get is:
#line_number #entity
3: Method: C1#m1c1
2: Class: M1::C1
12: Method: C2#m1c2
11: Class: M1::C2
16: Class: M1::C3
1: Module: M1
So you only need to customize the template, and extract the parameters which are available in the same array:
#irb > pp Ripper.sexp("def method(param1);nil; end")
...[:def,
[:#ident, "method", [1, 4]],
[:paren,
[:params, [[:#ident, "param1", [1, 11]]]...
Little bit harder task is to find out what is returned, but still doable - look for :returns while you have :def last in the stack and add it to the last statement of the method.
And finally put those comments above apropriate lines to the source file.

Related

Print editable to console in Ruby [duplicate]

This question already has answers here:
What will give me something like ruby readline with a default value?
(6 answers)
Closed 6 years ago.
Let's say I have the following code in Ruby:
print("Enter a filename:")
editableprint("untitled.txt")
filename = gets.chomp!
What would be the function "editableprint" so that "untitled.txt" is part of the input of the user for the gets function? (thus the user can edit the "untitled.txt" string or simply leave it as is")
There are similar questions here and here
However, the solutions there don't seem to work as expected, so it looks this is ruby version or platform dependent?
For example, this does not work for me, but also does not throw an error.
require "readline"
filename = Readline.insert_text("untitled.txt").readline("Enter a filename:")
print filename
But since it looks much better, and should work according to the documentation for ruby >= 2, I am leaving it there for now.
The following works on my system (ruby 2.3.1, OS X)
require "readline"
require 'rb-readline'
module RbReadline
def self.prefill_prompt(str)
#rl_prefill = str
#rl_startup_hook = :rl_prefill_hook
end
def self.rl_prefill_hook
rl_insert_text #rl_prefill if #rl_prefill
#rl_startup_hook = nil
end
end
RbReadline.prefill_prompt("untitled.txt")
str = Readline.readline("Enter a filename:", true)
puts "You entered: #{str}"
I would use vim to edit the file. Vim will save edited files in ~/.viminfo. The last edited file is marked with '0. The pattern of a file entry is 'N N N filename where N stands for a integer.
def editableprint(filename)
system "vi #{filename}"
regex = /(?<='0\s{2}\d\s{2}\d\s{2}).*/
viminfo = File.expand_path("~/.viminfo")
File.read(viminfo).scan(regex).first
end
In order to make this to work you would have to change your code
print("Enter a filename:")
filename = gets.chomp!
filename = "untitled.txt" if filename.emtpy?
edited_filename = editableprint("untitled.txt")

How to pass to CSV file only the first five tracks? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
require "openssl"
require "nokogiri"
require 'csv'
require "open-uri"
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
$n=0
#~ Open_Page
page = ('http://www.residentadvisor.net/dj/aguycalledgerald/tracks?sort=mostcharted')
html = Nokogiri::HTML(open(page))
#~ Array
names= []
html.css('a').each do |x|
names<< x.text.strip.gsub(/\t/,'')
names.delete('RA on YouTube')
names.delete('Login')
names.delete('Register')
names.delete('Resident Advisor')
names.delete('Submit')
names.delete('Listings')
names.delete('Clubs')
names.delete('News')
names.delete('Reviews')
names.delete('Features')
names.delete('Films')
names.delete('Submit event')
names.delete('Artists')
names.delete('Photos')
names.delete('DJ Charts')
names.delete('Labels')
names.delete('Podcasts')
names.delete('Search')
names.delete('Top 1000')
names.delete('Top 100')
names.delete('Local')
names.delete('Favourites')
names.delete('Create an artist profile')
names.delete('Reviews')
names.delete('Features')
names.delete('A')
names.delete('B')
names.delete('C')
names.delete('D')
names.delete('E')
names.delete('F')
names.delete('G')
names.delete('H')
names.delete('I')
names.delete('J')
names.delete('K')
names.delete('L')
names.delete('M')
names.delete('N')
names.delete('O')
names.delete('P')
names.delete('Q')
names.delete('R')
names.delete('S')
names.delete('T')
names.delete('U')
names.delete('V')
names.delete('W')
names.delete('X')
names.delete('Y')
names.delete('Z')
names.delete('0-9')
names.delete('RA')
names.delete('About')
names.delete('Advertise')
names.delete('Jobs')
names.delete('RA In Residence')
names.delete('Ticketing FAQ')
names.delete('Sell tickets on RA')
names.delete('Privacy')
names.delete('Terms')
names.delete('RA is also available in Japanese. 日本版')
names.delete('Download the RA Guide')
names.delete('RA on Twitter')
names.delete('RA on Facebook')
names.delete('RA on Google+')
names.delete('RA on Instagram')
names.delete('RA on Soundcloud')
names.delete('Biography')
names.delete('Events')
names.delete('Tracks')
names.delete('RA News')
names.delete('RA Editorial')
names.delete('Remixes')
names.delete('Solo productions')
names.delete('Collaborations')
names.delete('Laboratory Instinct')
names.delete('Highgrade Records')
names.delete('Bosconi')
names.delete('!K7')
names.delete('Perlon')
names.delete('Beatstreet')
names.delete('Title')
names.delete('Label')
names.delete('Release Date')
names.delete('51 chartings')
puts names
end
#~ To_CSV
for $n in 0..names.count do
CSV.open('Most_Charted.csv','a+') do |csv|
csv << [names[$n]]
end
end
That creates a CSV file with:
PositiveNoise (Carl Craig remix) System 7 & Guy Called Gerald A-Wave 22 chartings
Voodoo Ray (Shield Re-Edit) A Guy Called Gerald 18 chartings
Falling (D. Digglers Cleptomania remix) Tom Clark & Benno Blome feat.
A Guy Called Gerald 18 chartings
How Long Is Now A Guy Called Gerald 14 chartings
Groove Of The Ghetto A Guy Called Gerald 12 chartings
Voodoo Ray A Guy Called Gerald 10 chartings
Falling (D Diggler's Rescue remix) Tom Clark & Benno Blome feat. A
Guy Called Gerald 9 chartings
and so on.
How do I pass only the first 5 song names to the CSV file?
Be sure you know what you are doing when you disable SSL checks.
You can find a better selector for the track list, so you do not need all those "deletes". The tracks are all inside ul.tracks
Then i'd suggest you make the whole thing a class. So you can encapsulate the behavior. And then don't use $ globals. Not needed and usually a sign of bad code.
Here is a working sample:
require "openssl"
require "nokogiri"
require 'csv'
require "open-uri"
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
class Tracklist
def initialize(url)
#url = url
end
def parse(top = nil)
html = Nokogiri::HTML(open(url))
result = []
html.css('ul.tracks li').each do |node|
title = node.css('div.title a:nth-child(1)').first
result << title.text if !title.nil?
break if top && result.length == top
end
result
end
private
attr_reader :url
end
list = Tracklist.new("https://www.residentadvisor.net/dj/aguycalledgerald/tracks?sort=mostcharted")
p list.parse(5)
If you need more information about the tracks, then you can extract more details in the loop inside the parsemethod.
This code stops parsing after top has been reached. Afterwards you can build your CSV as you like.

How to retrieve value of XML element using a Nokogiri SAX Parser? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
How does one access the text value of a nested element using a Nokogiri SAX parser?
require 'nokogiri'
xml = <<-eos
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>http://www.example.com/example-sitemap.xml</loc>
</sitemap>
</sitemapindex>
eos
class MySAXDoc < Nokogiri::XML::SAX::Document
def start_element name, attrs=[]
if name == "sitemap"
# from here, how can one retrieve the value of the child element, `loc`?
end
end
end
sax_parser = Nokogiri::XML::SAX::Parser.new(MySAXDoc.new)
sax_parser.parse(xml)
You can't read ahead, so you must keep track of the current context within the file yourself. Something along these lines should do the trick:
def start_element(name, attrs = [])
#element = name
if name == 'sitemap'
#sitemap = true
end
end
def end_element(name)
#element = nil
if name == 'sitemap'
#sitemap = false
end
end
def characters(string)
if #element == 'loc' && #sitemap
# The local variable 'string' holds the text contents of the <loc> tag
# so do something with it here
puts string
end
end
How this works: When a new element is started it checks to see if it is a and if so sets a #sitemap variable. On the next iteration when the element is it checks #sitemap to see if it is within a sitemap and does something with its contents.

Argument should be a vector [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 years ago.
Improve this question
I'm trying to use the statsample library, but having issues with arrays/vectors.
b = [2,3,4,5,6,7].to_scale
# => #<TypeError: Argument should be a Vector>
Do you know why I might be getting this error?
EDIT 1
Something odd is going on in my environment....
$ irb
irb(main):001:0> require 'statsample'
=> true
irb(main):004:0> b = [2,3,4,5,6,7].to_scale
=> Vector(type:scale, n:6)[2,3,4,5,6,7]
exit
$ bundle exec irb
irb(main):001:0> b = [2,3,4,5,6,7].to_scale
NoMethodError: undefined method `to_scale' for [2, 3, 4, 5, 6, 7]:Array
from (irb):1
from /Users/brandon/.rbenv/versions/1.9.3-p484/bin/irb:12:in `<main>'
irb(main):002:0>
For some reason statsample is not being required when I use bundle exec. I have to manually require 'statsample in my code, even though gem 'statsample is in my Gemfile.
Any thoughts??
I don't see the issue:
irb(main):004:0> require 'statsample'
=> true
irb(main):004:0> b = [2,3,4,5,6,7].to_scale
=> Vector(type:scale, n:6)[2,3,4,5,6,7]
Please make sure that if you use the bundler, put into the Gemfile the following:
gem 'statsample'
And execute the bundle install.
According to the source code:
module Statsample::VectorShorthands
# Creates a new Statsample::Vector object
# Argument should be equal to Vector.new
def to_vector(*args)
Statsample::Vector.new(self,*args)
end
# Creates a new Statsample::Vector object of type :scale
def to_scale(*args)
Statsample::Vector.new(self, :scale, *args)
end
end
class Array
include Statsample::VectorShorthands
end
So here my guess is:
If it's just [Array].to_scale, it should have no problem at all. Unless you pass any argument to to_scale() which is not Vector type, because inside it's calling Statsample::Vector.new(self, :scale, *args), and it's saying "Argument should be equal to Vector.new".

How do I return a value from inside a loop? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am trying to read an XML file and store the structure into an array of objects. Here is my code:
class Bike
attr_accessor :id, :color, :name
def initialize(id, color, name)
#id = id
#color = color
#name = name
end
end
---x---snip---x---
rules.root.each_element do |node1|
case node1.name
when "Bike"
bike = Bike.new(node1.attributes['id'], node1.attributes['color'], { |bike_elem| bike_elem.text.chomp if bike_elem.name == "name"})
bikes.push bike
end
end
However, the last element is not fetching the value alone. It is fetching the whole tag. Is there a better way to do it?
Your code block { |bike_elem| bike_elem.text.chomp if bike_elem.name == "name" }
doesn't seem to make sense inside the new parameter list.
Where does bike_elem come from? This is not valid Ruby in this context.
It's hard to give an answer here without knowing what your XML looks like.
But I would recommend using a XML library like Nokogiri, libxml, and then parse out
the name before you do the new. Try using XPath.
rules.root.each_element do |node1|
case node1.name
when "Bike"
name = node1.attributes[....]
bike = Bike.new(node1.attributes['id'], node1.attributes['color'],name )
bikes.push bike
end
end

Resources