How to count lines of code? - ruby-on-rails

I tried rake stats but that seems highly inaccurate. Perhaps it ignores several directories?

I use the free Perl script cloc. Sample usage:
phrogz$ cloc .
180 text files.
180 unique files.
77 files ignored.
http://cloc.sourceforge.net v 1.56 T=1.0 s (104.0 files/s, 19619.0 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Javascript 29 1774 1338 10456
Ruby 61 577 185 4055
CSS 10 118 133 783
HTML 1 13 3 140
DOS Batch 2 6 0 19
Bourne Shell 1 4 0 15
-------------------------------------------------------------------------------
SUM: 104 2492 1659 15468
-------------------------------------------------------------------------------

Here's a simple solution. It counts the lines of code in your rails project's app folder - CSS, Ruby, CoffeeScript, and all. At the root of your project, run this command:
find ./app -type f | xargs cat | wc -l
EDIT
Read the comments. Then try this instead:
find ./app -type f -name "*.rb" | xargs cat | sed "/^\s*\(#\|$\)/d" | wc -l

You can try out these two options:
Hack rake stats
Rakestats snippet from blogpost:
namespace :spec do
desc "Add files that DHH doesn't consider to be 'code' to stats"
task :statsetup do
require 'code_statistics'
class CodeStatistics
alias calculate_statistics_orig calculate_statistics
def calculate_statistics
#pairs.inject({}) do |stats, pair|
if 3 == pair.size
stats[pair.first] = calculate_directory_statistics(pair[1], pair[2]); stats
else
stats[pair.first] = calculate_directory_statistics(pair.last); stats
end
end
end
end
::STATS_DIRECTORIES << ['Views', 'app/views', /\.(rhtml|erb|rb)$/]
::STATS_DIRECTORIES << ['Test Fixtures', 'test/fixtures', /\.yml$/]
::STATS_DIRECTORIES << ['Email Fixtures', 'test/fixtures', /\.txt$/]
# note, I renamed all my rails-generated email fixtures to add .txt
::STATS_DIRECTORIES << ['Static HTML', 'public', /\.html$/]
::STATS_DIRECTORIES << ['Static CSS', 'public', /\.css$/]
# ::STATS_DIRECTORIES << ['Static JS', 'public', /\.js$/]
# prototype is ~5384 LOC all by itself - very hard to filter out
::CodeStatistics::TEST_TYPES << "Test Fixtures"
::CodeStatistics::TEST_TYPES << "Email Fixtures"
end
end
task :stats => "spec:statsetup"
metric_fu - A Ruby Gem for Easy Metric Report Generation
PS: I haven't tried any of the above, but metric_fu sounds interesting, see the screenshots of the output.

This one calculates number of files, total lines of code, comments, and average LOC per file. It also excludes files inside directories with "vendor" in their name.
Usage:
count_lines('rb')
Code:
def count_lines(ext)
o = 0 # Number of files
n = 0 # Number of lines of code
m = 0 # Number of lines of comments
files = Dir.glob('./**/*.' + ext)
files.each do |f|
next if f.index('vendor')
next if FileTest.directory?(f)
o += 1
i = 0
File.new(f).each_line do |line|
if line.strip[0] == '#'
m += 1
next
end
i += 1
end
n += i
end
puts "#{o.to_s} files."
puts "#{n.to_s} lines of code."
puts "#{(n.to_f/o.to_f).round(2)} LOC/file."
puts "#{m.to_s} lines of comments."
end

If your code is hosted on GitHub, you can use this line count website. Just enter your GitHub URL and wait for the result.
Example for Postgres: https://line-count.herokuapp.com/postgres/postgres
File Type Files Lines of Code Total lines
Text 1336 0 472106
C 1325 1069379 1351222
Perl 182 23917 32443
Shell 5 355 533
...

Related

Migrating uploaded files from Active Storage to Carrierwave

For a variety of reasons I am migrating my uploads from ActiveStorage (AS) to CarrierWave (CW).
I am making rake task and have the logic sorted out - I am stumped at how to feed the AS blob into the CW file.
I am trying something like ths:
#files.each.with_index(1) do | a, index |
if a.attachment.attached?
a.attachment.download do |file|
a.file = file
end
a.save!
end
end
This is based on these two links:
https://edgeguides.rubyonrails.org/active_storage_overview.html#downloading-files
message.video.open do |file|
system '/path/to/virus/scanner', file.path
# ...
end
and
https://github.com/carrierwaveuploader/carrierwave#activerecord
# like this
File.open('somewhere') do |f|
u.avatar = f
end
I tested this locally and the files are not mounted via the uploader. My question(s) would be:
am I missing something obvious here?
is my approach wrong and needs a new one?
Bonus Karma Question:
I can't seem to see a clear path to set the CW filename when I do this?
Here is my final rack task (based on the accepted answer) - open to tweaks. Does the job for me:
namespace :carrierwave do
desc "Import the old AS files into CW"
task import: :environment do
#files = Attachment.all
puts "#{#files.count} files to be processed"
puts "+" * 50
#files.each.with_index(1) do | a, index |
if a.attachment.attached?
puts "Attachment #{index}: Key: #{a.attachment.blob.key} ID: #{a.id} Filename: #{a.attachment.blob.filename}"
class FileIO < StringIO
def initialize(stream, filename)
super(stream)
#original_filename = filename
end
attr_reader :original_filename
end
a.attachment.download do |file|
a.file = FileIO.new(file, a.attachment.blob.filename.to_s)
end
a.save!
puts "-" * 50
end
end
end
desc "Purge the old AS files"
task purge: :environment do
#files = Attachment.all
puts "#{#files.count} files to be processed"
puts "+" * 50
#files.each.with_index(1) do | a, index |
if a.attachment.attached?
puts "Attachment #{index}: Key: #{a.attachment.blob.key} ID: #{a.id} Filename: #{a.attachment.blob.filename}"
a.attachment.purge
puts "-" * 50
#count = index
end
end
puts "#{#count} files purged"
end
end
Now in my case I am doing this in steps - I have branched my master with this rake task and the associated MCV updates. If my site was in true production would probably run the import rake task first then confirm all went well THEN purge the old AS files.
The file object you get from the attachment.download block is a string. More precisely, the response from .download is the file, "streamed and yielded in chunks" (see documentation). I validated this by calling file.class to make sure the class is what I expected.
So, to solve your issue, you need to provide an object on which .read can be called. Commonly that is done using the Ruby StringIO class.
However, considering Carrierwave also expects a filename, you can solve it using a helper model that inherits StringIO (from blogpost linked above):
class FileIO < StringIO
def initialize(stream, filename)
super(stream)
#original_filename = filename
end
attr_reader :original_filename
end
And then you can replace a.file = file with a.file = FileIO.new(file, 'new_filename')

Is it possible to render/generate pdf in rails without using a gem?

There are some gems which can generate pdf. But I don't want to use a gem. I tried the following:
def show
respond_to do |format|
format.html
format.pdf
end
end
And for the link:
link_to show_path(#show, format: :pdf)
I can get the pdf output but it says the pdf document might not be displayed properly.
Rails does not generate pdf out-of-the box. PDF is a 7-bit text format with binary parts, so technically you can generate it manually using ERB-templates, show.pdf.erb:
%PDF-1.1
%¥±ë
1 0 obj << /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 300 300] >>
endobj
3 0 obj << /Type /Page /Parent 2 0 R
/Resources
<< /Font
<< /F1
<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman >>
>>
>>
/Contents 4 0 R
>>
endobj
4 0 obj << /Length 90 >>
stream
BT
/F1 18 Tf
90 150 Td
(Hello World!) Tj
ET
endstream
endobj
trailer << /Root 1 0 R /Size 4 >>
%%EOF
This minimal PDF is viewable by some apps, but has errors in it, because there's no xref section and object bytecounts will be wrong. Also once you need anything more complex than single page with couple text labels on it - it will become hard to maintain.
Better way of generation is actually using a gem like prawn or wicked_pdf

[RUBY]Server/Client | Command Client to start Using function

So I am working on some stupid simulation of mining cryptocoins - just for fun.
But I have run into an issue I want to command the client to start using a function but I am not sure how to do it, my code is bellow for Server.rb and Client.rb
Client
require 'socket'
s = TCPSocket.new 'localhost', 2626
while line = s.gets
puts line
end
s.close
Server.rb
#!/bin/ruby
require "socket"
require 'securerandom'
PORT = 2626
server_pool = TCPServer.open(PORT)
sndc_block_time = 200 # Size of block
sndc_block_time = 10 # Time to brake block 1,1 out of 200, 200
sndc_coin_blocks= 126^2 # Ammount of available blocks
sndc_coin_balance = 0
sub_blocks = 0 # Sub blocks user wants to mine
addr = SecureRandom.hex # generate a random hexadecimal address
# Notify message shown when user registers a new address
disclaimer = "Welcome to the SendCoin Network!
A new address has been registered and saved
to your computer desktop!"
def handle_connection(client)
puts "New client! #{client} \n\n"
end
puts "Listening on #{PORT}. Press CTRL+C to cancel."
while client = server_pool.accept
if Thread.new { handle_connection(client) }
if sndc_coin_blocks < 126^2 / 4
sndc_coin_blocks + 126^2 / 4 # Add a quarter of the original size
end
if Dir['../MyAddress/*.addr'].any? == true
# Statrs Mining
client.puts "Starting to mine.."
sleep(5)
client.puts "Current blocks: " + sndc_coin_blocks.to_s
client.puts "Block Size: " + sndc_coin_balance.to_s
client.puts "Approximate time to mine 1 sub-block: " + sndc_block_time.to_s
client.puts "Searching for block.."
sleep(3)
if sndc_block_time != 10
sndc_block_time = 10
end
client.puts "Started mining..."
elsif Dir['../addresses/*.addr'].any? == false
File.open("../addresses/"+addr+".addr", "w") do |f| # Create file
f.write(sndc_coin_balance.to_s)
end
client.puts "New address generated: " + addr.to_s + " , you may realunch the app now and enter your address!"
# Start Mining
puts "> File 'address.addr' has been generated for#{client}"
end
end
end
So essentially I want after this line of code:
client.puts "Started mining..."
.. to command the client to (client.rb) to start using a function.

How to Calculate sum of all the digits in text file

I am having text file t.txt,I want to calculate sum of all the digits in text file
Example
--- t.txt ---
The rahul jumped in 2 the well. The water was cold at 1 degree Centigrade. There were 3 grip holes on the walls. The well was 17 feet deep.
--- EOF --
sum 2+1+3+1+7
My ruby code to calculate sum is
ruby -e "File.read('t.txt').split.inject(0){|mem, obj| mem += obj.to_f}"
But i am not getting any answer??
str = "The rahul jumped in 2 the well. The water was cold at 1 degree Centigrade. There were 3 grip holes on the walls. The well was 17 feet deep."
To get sum of all integers:
str.scan(/\d+/).sum(&:to_i)
# => 23
Or to get sum of all digits as in your example:
str.scan(/\d+?/).sum(&:to_i)
# => 14
PS: I used sum seeing Rails tag. If you are only using Ruby you can use inject instead.
Example with inject
str.scan(/\d/).inject(0) { |sum, a| sum + a.to_i }
# => 14
str.scan(/\d+/).inject(0) { |sum, a| sum + a.to_i }
# => 23
Your statement is computing correctly. Just add puts before File read as:
ruby -e "puts File.read('t.txt').split.inject(0){|mem, obj| mem += obj.to_f}"
# => 23.0
For summing single digit only:
ruby -e "puts File.read('t.txt').scan(/\d/).inject(0){|mem, obj| mem += obj.to_f}"
# => 14.0
Thanks

Open3.popen3 function to open bz, gz, and txt files errors with 'No such file or directory' or 'not opened for reading'?

I'm trying to write a utility function that will open three different types of files: .bz2, .gz, and .txt. I can't just use File.read because it gives me garbage back for the compressed files. I'm trying to use Open3.popen3 so that I can give it a different command, but I'm getting a 'no such file or directory' error with the following code:
def file_info(file)
cmd = ''
if file.match("bz2") then
cmd = "bzcat #{file}"# | head -20"
elsif file.match("gz") then
cmd = "gunzip -c #{file}"
else
cmd = "cat #{file}"
end
puts "opening file #{file}"
Open3.popen3("#{cmd}", "r+") { |stdin, stdout, stderr|
puts "stdin #{stdin.inspect}"
stdin.read {|line|
puts "line is #{line}"
if line.match('^#') then
else
break
end
}
}
end
> No such file or directory - cat /tmp/test.txt
The file does exist. I've tried using cmd instead of #{cmd} with the same results in the popen3 cmd.
I decided to hardcode it to do the txt file as follows:
def file_info(file)
puts "opening file #{file}"
Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr|
puts "stdin #{stdin.inspect}"
stdin.read {|line|
puts "line is #{line}"
if line.match('^#') then
else
break
end
}
}
end
This gives me back:
stdin #<IO:fd 6>
not opened for reading
What am I doing wrong?
When I do:
Open3.popen3("cat",file) { |stdin, stdout, stderr|
puts "stdout is #{stdout.inspect}"
stdout.read {|line|
puts "line is #{line}"
if line.match('^#') then
puts "found line #{line}"
else
break
end
}
}
I get no errors and the STDOUT line is printed, but neither line statement prints out anything.
After trying several different things, the solution I came up with was:
cmd = Array.new
if file.match(/\.bz2\z/) then
cmd = [ 'bzcat', file ]
elsif file.match(/\.gz\z/) then
cmd = [ 'gunzip', '-c', file ]
else
cmd = [ 'cat', file ]
end
Open3.popen3(*cmd) do |stdin, stdout, stderr|
puts "stdout is #{stdout}"
stdout.each do |line|
if line.match('^#') then
puts "line is #{line}"
else
break
end
end
end
From the fine manual (which is rather confusingly written):
*popen3(cmd, &block)
[...]
So a commandline string and list of argument strings can be accepted as follows.
Open3.popen3("echo a") {|i, o, e, t| ... }
Open3.popen3("echo", "a") {|i, o, e, t| ... }
Open3.popen3(["echo", "argv0"], "a") {|i, o, e, t| ... }
So when you do this:
Open3.popen3("cat /tmp/test.txt", "r+")
popen3 thinks that the command name is cat /tmp/test.txt and r+ is an argument to that command, hence the specific error that you're seeing:
No such file or directory - cat /tmp/test.txt
There's no need for the usual mode flags ("r+") with Open3.popen3 since it will separate handles for reading, writing, and errors; and, as you've seen, trying to supply the mode string just causes bugs and confusion.
The second case:
Open3.popen3("cat", file, "r+") { |stdin, stdout, stderr|
stdin.each {|line|
#...
Doesn't work because stdin is the command's standard input and that's what you would write to not read from, you'd want to stdout.read instead.
You should be building your commands as arrays and your match calls should be a little stricter:
if file.match(/\.bz2\z/) then
cmd = [ 'bzcat', file ]
elsif file.match(/\.gz\z/) then
cmd = [ 'gunzip', '-c', file ]
else
cmd = [ 'cat', file ]
end
and then splat them:
Open3.popen3(*cmd) do |stdin, stdout, stderr|
#...
end
Not only does this work but it will save you from funny filenames.
You could also avoid a useless use of cat (which someone will probably complain about) by skipping the Open3.popen3 for the non-compressed cases and using File.open instead. You might also want to consider checking the file's bytes to see what it contains rather than relying on the extension (or use ruby-filemagic to check for you).
You'd better use bzip2-ruby and GzipReader for reading corresponding files. Opening a separate process for that is too expensive, complex and fragile.

Resources