How to dividie entries into different pages in Rails? - ruby-on-rails

For example, I have 30 entries, and I wish to divide them so that it only displays 3 of them on each page (and having many pages of course).
Currently, I have implemented it by passing a parameter in the URL and writing Ruby code in the action.
x = params[:id]
if x
#problems = []
x = params[:id].to_i
t = Problem.all.count
i = 1
problem_numbers = -3 * (x - 1)
while t > 0 do
if Problem.exists?(i)
problem_numbers += 1
t -= 1
if problem_numbers > 0
#problems = #problems + [Problem.find(i)]
end
end
if problem_numbers == 3
break
end
i += 1
end
end
It works quite well, but I think this piece of code might a bit complicated for such a feature; also if there are many entries, visiting pages would be slow because I counted through all the entries.
Is there a more convenient way to do this? Thanks in advance.

Displaying items across multiple pages is called pagination. will_paginate and Kaminari are two very popular gems that provide this functionality in a way that is very easily integrated into Ruby on Rails applications.

Related

How to use Rails Web App as ruby script input and output

I'm new to rails and I'm working on this project:
I have done a ruby script which needs an input and that prints some outputs.
Basically it takes the first n decimal digits of PI and then it calculates some stats.
I need to use a rails web app to insert the input, which is a number, and to print the decimal digits and 10 stats rows.
This is the ruby script
require 'bigdecimal/math'
#PI decimals generator
i = 0
j = 0
a_pi = Array.new
print "Insert how many digits do you want to calculate: "
dig_n = gets.chomp.to_i
x = BigMath.PI(dig_n)
a_pi = x.to_s.split('')
begin
a_pi.shift
i += 1
end until i == 3
arr_length = a_pi.length
begin
a_pi.pop
j += 1
end until j == arr_length - dig_n
pi_dec = a_pi.join.to_i
puts pi_dec
I'm using Ruby 2.6 and Rails 6
I think you are thinking about this the wrong way around. What you want is more "a web app/web form that does something with input". Rails might be too much for your use case. If you are not bound to Rails you might want to look into other / lighter web frameworks in ruby like Sinatra or Hanami. This could help: Run ruby script from a HTML form

Generated integer array based on number in rails

I want to generate 5 buttons with different values based on one integer.
For example I've got 30, I want to create buttons with 10 20 30 40 50
value = 30
int1 = value - 20
int2 = value - 10
int3 = value
int4 = value + 10
int5 = value + 20
buttoncode = ""
%w{int1 int2 int3 int4 int5}.each do |minutes|
buttoncode += 'buttoncode'
end
I can do it in a very bad way, but it could be done a smarter solution I guess.
Is it possible to make something like that?
%w{sum(max-20) sum(max-10) max sum(max+10) sum(max+20)}.each do |minutes|
end
See Ruby: How to iterate over a range, but in set increments?
So in your case it would be:
(min..max).step(10) do |n|
n += 'buttoncode'
end
By the way, this is not really Rails specific, but Ruby specific. Rails is a web framework that handles the interaction between browser and the web server that is built on top of Ruby.
If you feel like you aren't that up to speed with Ruby, try https://learnrubythehardway.org/book/ and do some exercise on HackerRank or ProjectEuler in Ruby.

Ruby on Rails random number not working

I've been at this for awhile. I am building a simple lottery website and I am generating random tickets. On my local machine random numbers are generated, however, on the server they are duplicated.
I have tried multiple versions of what I have, but the duplicates is the same.
I need to create a random ticket number per ticket and ensure that it hasn't bee created.
This my like 50th version:
a = Account.find(current_account)
numTics = params[:num_tickets].to_i
t = a.tickets.where(:item_id => item.id).count
total = t + numTics
if total > 5
left = 5 - t
flash[:message] = "The total amount of tickets you can purchase per item is five. You can purchase #{left} more tickets."
redirect_to buy_tickets_path(item.id)
else
i = 1
taken = []
random = Random.new
taken.push(random.rand(100.10000000000000))
code = 0
while i <= numTics do
while(true)
code = random.rand(100.10000000000000)
if !taken.include?(code)
taken.push(code)
if Ticket.exists?(:ticket_number => code) == false
a.tickets.build(
:item_id => item.id,
:ticket_number => code
)
a.save
break
end
code = 0
end
end
i = i + 1
end
session['item_name'] = item.name
price = item.price.to_i * 0.05
total = price * numTics
session['amount_due'] = total.to_i
redirect_to confirmation_path
end
You should be using SecureRandom if possible, not Random. It works the same way but is much more random and doesn't need to be initialized like Random does:
SecureRandom.random_number * 100.1
If you're using Ruby 1.8.7 you can try the ActiveSupport::SecureRandom equivalent.
Also if you're generating lottery tickets, you will want to make sure your generator is cryptographically secure. Generating random numbers alone is probably not sufficient. You will likely want to apply some other function to generate these.
Keep in mind that most actual lotteries do not generate random tickets at the point of purchase, but generate large batches in advance and then issue these to purchasers. This means you are able to preview the tickets and ensure they are sufficiently random.
The problem is not with Ruby's pseudo random number generator but that fact that you are creating generators all the time with Random.new. As explained in this answer, you should not have to call Random.new more than once. Store the result in a global object and you'll be good to go.

How to read a file from bottom to top in Ruby?

I've been working on a log viewer for a Rails app and have found that I need to read around 200 lines of a log file from bottom to top instead of the default top to bottom.
Log files can get quite large, so I've already tried and ruled out the IO.readlines("log_file.log")[-200..-1] method.
Are there any other ways to go about reading a file backwards in Ruby without the need for a plugin or gem?
The only correct way to do this that also works on enormous files is to read n bytes at a time from the end until you have the number of lines that you want. This is essentially how Unix tail works.
An example implementation of IO#tail(n), which returns the last n lines as an Array:
class IO
TAIL_BUF_LENGTH = 1 << 16
def tail(n)
return [] if n < 1
seek -TAIL_BUF_LENGTH, SEEK_END
buf = ""
while buf.count("\n") <= n
buf = read(TAIL_BUF_LENGTH) + buf
seek 2 * -TAIL_BUF_LENGTH, SEEK_CUR
end
buf.split("\n")[-n..-1]
end
end
The implementation is a little naive, but a quick benchmark shows what a ridiculous difference this simple implementation can already make (tested with a ~25MB file generated with yes > yes.txt):
user system total real
f.readlines[-200..-1] 7.150000 1.150000 8.300000 ( 8.297671)
f.tail(200) 0.000000 0.000000 0.000000 ( 0.000367)
The benchmark code:
require "benchmark"
FILE = "yes.txt"
Benchmark.bmbm do |b|
b.report "f.readlines[-200..-1]" do
File.open(FILE) do |f|
f.readlines[-200..-1]
end
end
b.report "f.tail(200)" do
File.open(FILE) do |f|
f.tail(200)
end
end
end
Of course, other implementations already exist. I haven't tried any, so I cannot tell you which is best.
There's a module Elif available (a port of Perl's File::ReadBackwards) which does efficient line-by-line backwards reading of files.
Since I'm too new to comment on molf awesome answer I have to post it as a separate answer.
I needed this feature to read log files while they're written , and the last portion of the logs contain the string I need to know it's done and I can start parsing it.
Hence handling small sized files is crucial for me (I might ping the log while it's tiny).
So I enhanced molf code:
class IO
def tail(n)
return [] if n < 1
if File.size(self) < ( 1 << 16 )
tail_buf_length = File.size(self)
return self.readlines.reverse[0..n-1]
else
tail_buf_length = 1 << 16
end
self.seek(-tail_buf_length,IO::SEEK_END)
out = ""
count = 0
while count <= n
buf = self.read( tail_buf_length )
count += buf.count("\n")
out += buf
# 2 * since the pointer is a the end , of the previous iteration
self.seek(2 * -tail_buf_length,IO::SEEK_CUR)
end
return out.split("\n")[-n..-1]
end
end

Prime Number - Data while loading

I was trying in Ruby on Rails how to find a prime number. Here is my code :
helper : app/helpers/test_helper.rb
module TestHelper
def prime_number? number
index = 2
tmp = 0
while index <= number
if tmp < 1
if (number % index) == 0
tmp += 1
end
else
return false
end
index += 1
end
return true
end
end
and my view : app/views/test/index.html.erb
<% (2..100).each do |i| -%>
<% if prime_number? i %>
<%= i %>
<% end -%>
<% end -%>
So my question is : How can you load data while it's calculating ? I mean if I replace 100 by 100000 in my view, how can I see data on my view while my helper method is calculating ?
Do I need to use ajax or rails provide a tool for that ?
Thank you.
Short answer: yes, you'll need AJAX.
Longer answer: you can use the Prototype Helper periodically_call_remote to poll one of your actions and add new prime numbers to the end of the list.
There is an issue though, Ruby is very slow compared to native OS languages such as C. If you want to get the most prime number results, I'd write the prime number code in C and find a mechanism to access the results. One way to do that would be to have your C application write prime numbers to a file and you can tail -f that file to get the new primes. I'm sure you can find a C example for finding prime numbers somewhere.
Even if you were to write the prime number code in Ruby, you'd have to run it as a separate process from your Rails application for it to continually calculate primes, so you might as well do it in something faster. Actually, Ruby might not be too terribly slow for this calculation, it'd be interesting to benchmark the two.

Resources