rails ,paginate, sort and more than one table - mysql

I'm new to rails, I want to know how to paginate and sort by a particular column.
I can do this within one table, but when there is more than table, and the sorted column not in the main table.
I have tried many times but failed, and I also searched the web, but also in vain. Now I need your help...help please!
while paginate i'm using this:
# encoding: utf-8
module Pagination
class Paginator
attr_reader :item_count, :per_page, :page, :page_param
def initialize(*args)
if args.first.is_a?(ActionController::Base)
args.shift
ActiveSupport::Deprecation.warn "Paginator no longer takes a controller instance as the first argument. Remove it from #new arguments."
end
item_count, per_page, page, page_param = *args
#item_count = item_count
#per_page = per_page
page = (page || 1).to_i
if page < 1
page = 1
end
#page = page
#page_param = page_param || :page
end
def offset
(page - 1) * per_page
end
def first_page
if item_count > 0
1
end
end
def previous_page
if page > 1
page - 1
end
end
def next_page
if last_item < item_count
page + 1
end
end
def last_page
if item_count > 0
(item_count - 1) / per_page + 1
end
end
def first_item
item_count == 0 ? 0 : (offset + 1)
end
def last_item
l = first_item + per_page - 1
l > item_count ? item_count : l
end
def linked_pages
pages = []
if item_count > 0
pages += [first_page, page, last_page]
pages += ((page-2)..(page+2)).to_a.select {|p| p > first_page && p < last_page}
end
pages = pages.compact.uniq.sort
if pages.size > 1
pages
else
[]
end
end
def items_per_page
ActiveSupport::Deprecation.warn "Paginator#items_per_page will be removed. Use #per_page instead."
per_page
end
def current
ActiveSupport::Deprecation.warn "Paginator#current will be removed. Use .offset instead of .current.offset."
self
end
end
# Paginates the given scope or model. Returns a Paginator instance and
# the collection of objects for the current page.
#
# Options:
# :parameter name of the page parameter
#
# Examples:
# #user_pages, #users = paginate User.where(:status => 1)
#
def paginate(scope, options={})
options = options.dup
finder_options = options.extract!(
:conditions,
:order,
:joins,
:include,
:select
)
if scope.is_a?(Symbol) || finder_options.values.compact.any?
return deprecated_paginate(scope, finder_options, options)
end
paginator = paginator(scope.count, options)
collection = scope.limit(paginator.per_page).offset(paginator.offset).to_a
return paginator, collection
end
def deprecated_paginate(arg, finder_options, options={})
ActiveSupport::Deprecation.warn "#paginate with a Symbol and/or find options is depreceted and will be removed. Use a scope instead."
klass = arg.is_a?(Symbol) ? arg.to_s.classify.constantize : arg
scope = klass.scoped(finder_options)
paginate(scope, options)
end
def paginator(item_count, options={})
options.assert_valid_keys :parameter, :per_page
page_param = options[:parameter] || :page
page = (params[page_param] || 1).to_i
per_page = options[:per_page] || per_page_option
Paginator.new(item_count, per_page, page, page_param)
end
module Helper
include Redmine::I18n
# Renders the pagination links for the given paginator.
#
# Options:
# :per_page_links if set to false, the "Per page" links are not rendered
#
def pagination_links_full(*args)
pagination_links_each(*args) do |text, parameters, options|
if block_given?
yield text, parameters, options
else
link_to text, params.merge(parameters), options
end
end
end
# Yields the given block with the text and parameters
# for each pagination link and returns a string that represents the links
def pagination_links_each(paginator, count=nil, options={}, &block)
options.assert_valid_keys :per_page_links
per_page_links = options.delete(:per_page_links)
per_page_links = false if count.nil?
page_param = paginator.page_param
html = ''
if paginator.previous_page
# \xc2\xab(utf-8) = «
text = "\xc2\xab " + l(:label_previous)
html << yield(text, {page_param => paginator.previous_page}, :class => 'previous') + ' '
end
previous = nil
paginator.linked_pages.each do |page|
if previous && previous != page - 1
html << content_tag('span', '...', :class => 'spacer') + ' '
end
if page == paginator.page
html << content_tag('span', page.to_s, :class => 'current page')
else
html << yield(page.to_s, {page_param => page}, :class => 'page')
end
html << ' '
previous = page
end
if paginator.next_page
# \xc2\xbb(utf-8) = »
text = l(:label_next) + " \xc2\xbb"
html << yield(text, {page_param => paginator.next_page}, :class => 'next') + ' '
end
html << content_tag('span', "(#{paginator.first_item}-#{paginator.last_item}/#{paginator.item_count})", :class => 'items') + ' '
if per_page_links != false && links = per_page_links(paginator, &block)
html << content_tag('span', links.to_s, :class => 'per-page')
end
html.html_safe
end
# Renders the "Per page" links.
def per_page_links(paginator, &block)
values = per_page_options(paginator.per_page, paginator.item_count)
if values.any?
links = values.collect do |n|
if n == paginator.per_page
content_tag('span', n.to_s)
else
yield(n, :per_page => n, paginator.page_param => nil)
end
end
l(:label_display_per_page, links.join(', ')).html_safe
end
end
def per_page_options(selected=nil, item_count=nil)
options = Setting.per_page_options_array
if item_count && options.any?
if item_count > options.first
max = options.detect {|value| value >= item_count} || item_count
else
max = item_count
end
options = options.select {|value| value <= max || value == selected}
end
if options.empty? || (options.size == 1 && options.first == selected)
[]
else
options
end
end
end
end
end

I think the Kaminari gem is a little bit better now than will_paginate. Take a look at that one too.

Haven't you tried 'will_paginate' gem? It seems clearer for me than whatever in your code, and it's well-documented.
Here it is, take a look: https://github.com/mislav/will_paginate

If you want to paginate elements by quantity, like "100 posts per page", will_paginate is the best solution, I think.
But the problem appears, if you try to paginate elements by date or so on, when you can't predict, how many elements will be on the page. In this case, I'm using this solution will_paginate can it order by day. Works well for me.

Related

Deserializing an object with other objects in it

I'm making a two-player game of chess played on the command line. I have a class for every type of chess piece and a board class. The board class looks like this:
class Board
attr_accessor :board, :choice
def initialize
#board = Array.new(8){Array.new(8," ")}
#choice = choice
end
end
My other classes look like this:
class Bishop
attr_accessor :x_position, :y_position, :piece, :color, :counter, :moves
def initialize(position,boolean)
#x_position = position[0]
#y_position = position[1]
#piece = boolean ? "♝" : "♗"
#color = boolean ? "white" : "black"
#counter = 0
#moves = [[+1,-1],
[+1,+1],
[-1,+1],
[-1,-1]]
end
I added my pieces to the board like this:
#board[0][0] = Rook.new([0,0],false)
These are my methods to serialize and deserialize the data:
def to_json
JSON.generate({board: #board})
end
def save_game(string)
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
end
def load_game
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#board = data["board"]
end
After saving, the saved.json file looks like this:
{"board":[[" ","#<Knight:0x00000000e4fc28>","#<Bishop:0x00000000e4fa20>","#<Queen:0x00000000e4f890>","#<King:0x00000000e4f610>","#<Bishop:0x00000000e4f3e0>","#<Knight:0x00000000e4f278>","#<Rook:0x00000000e4e1c0>"],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],[" "," "," "," "," "," "," "," "],["#<Rook:0x00000000e4fd90>","#<Knight:0x00000000e4ed78>","#<Bishop:0x00000000e4eb70>","#<Queen:0x00000000e4ea08>","#<King:0x00000000e4e7b0>","#<Bishop:0x00000000e4e580>","#<Knight:0x00000000e4e3f0>"," "]]}
When I try to load back the data, the method that displays the board throws this error:
0 1 2 3 4 5 6 7
+----+----+----+----+----+----+----+----+
0|   /home/jacob/Desktop/chess/board.rb:30:in `block (2 levels) in display': undefined method `piece' for "#<Knight:0x00000000e4fc28>":String (NoMethodError)
from /home/jacob/Desktop/chess/board.rb:27:in `each'
from /home/jacob/Desktop/chess/board.rb:27:in `each_with_index'
from /home/jacob/Desktop/chess/board.rb:27:in `block in display'
from /home/jacob/Desktop/chess/board.rb:20:in `each'
from /home/jacob/Desktop/chess/board.rb:20:in `each_with_index'
It looks like my problem is that the objects come back as strings?
my display method:
def display
axis = 0
print " 0 1 2 3 4 5 6 7"
#board.each_with_index do |row,index|
print "\n"
#draw = " +----+----+----+----+----+----+----+----+"
puts #draw
print axis
axis +=1
if index.even?
row.each_with_index do|column,i|
if i.odd?
if column != " "
print "|"+" #{column.piece} ".bruno
else print "|"+" #{column} ".bruno
end
else
if column != " "
print "|"+" #{column.piece} "
else print "|"+" #{column} "
end
end
end
else
row.each_with_index do|column,j|
if j.even?
if column != " "
print "|"+" #{column.piece} ".bruno
else print "|"+" #{column} ".bruno
end
else
if column != " "
print "|"+" #{column.piece} "
else print "|"+" #{column} "
end
end
end
end
print "|"
end
print "\n"
print #draw
end
No, the problem is that your objects are saved as strings. Deserialization works fine, in this case. You'll have to explicitly specify what should be the json representation of your game pieces. Something like this:
class Rook
def to_json(*)
{ name: 'rook', position: 'A1', status: 'in_game' }.to_json
end
end
pieces = [Rook.new]
pieces.to_json # => "[{\"name\":\"rook\",\"position\":\"A1\",\"status\":\"in_game\"}]"
JSON.parse(pieces.to_json) # => [{"name"=>"rook", "position"=>"A1", "status"=>"in_game"}]
On deserialization you'll have to do the reverse. Construct proper game classes from plain ruby hashes, which you get from parsing JSON file.
Or if you don't actually care about JSON and just want to create some form of save file, then Marshal is your best friend. No need to override anything. Zero friction.
pieces = [Rook.new]
Marshal.dump(pieces) # => "\x04\b[\x06o:\tRook\x00" # write this to a file
# restore it later
Marshal.load("\x04\b[\x06o:\tRook\x00") # => [#<Rook:0x007fb50f825570>]

Trying to serialize with JSON a save method in my Ruby Hangman game

I'm trying to get my save_game() method to work, nothing is getting written to the JSON file. This is my first assignment working with JSON and serialization in general. I'm not quite sure where I'm even going wrong.
These are my serialization methods:
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
This is my program, on line 92 I try to call my save_game method.
require 'json'
load 'display.rb'
class Hangman
attr_accessor :name
#name = name
def initialize
puts "What is your name?"
#name = gets.chomp
puts "
################################################
HANGMAN
################################################
_________
|
| |
| O
| /|\\
| |
| / \\
|
-----------------
Welcome #{#name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
"
end
end
class Gameplay
attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
def initialize
#array = []
#filestuff = File.foreach('5text.txt') do |x|
chomped = x.chomp
#array << chomped if (chomped.length >= 5 and chomped.length <= 12)
end
#random_word = #array.sample
#cipher = #random_word.gsub(/[a-z]/, '*').split(//)
#random_word2 = #random_word.split(//)
#counter = 5
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
def choice(n)
#random_word2.each_with_index do |i,index|
if i == n
#cipher[index] = i
end
end
if n == #random_word2.join.to_s
puts "You win"
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
Hangman.new
else exit
end
end
if #random_word2.include?(n) == false
#counter -= 1
display
puts "#{#counter} guesses remaining."
puts "To save press 1"
save = gets.chomp
if save == "1"
#Will not save
save_game($b.to_json)
end
end
if #counter == 0
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
else exit
end
end
puts #cipher.join
end
#counter = 5
while #counter > 0
choice(gets.chomp)
end
end
end
Hangman.new
$b = Gameplay.new
You need to close the file in order to make sure your output is actually written to the disk ("flushed"). You can manually, call close:
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
or, you can use File.open, which takes a block and closes the file when the block ends:
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
Since, writing to the disk is a slow operation, Ruby (and all languages that I can think of right now) will hold off on actually writing the file until it has accumulated a certain amount of text in a buffer. Once it has reached this limit, it will flush the buffer and write everything in it to disk. In order to make sure all your text is actually written when trying to write a file, you need to call close on the file, and as part of closing it, Ruby will flush whatever is left in its buffer.
There are other ways of making sure your content is flushed but when you're just starting to learn about this stuff, it should suffice to just make sure to always close files when you're done reading or writing them.

Odd rendering of an html table in Ruby on Rails when using content_tag

I'm trying to build a table in Ruby on Rails with the help of the content_tag method.
When I run this:
def itemSemanticDifferential(method, options = {})
if options[:surveyQuestion].any? then
#template.content_tag(:tr) do
#template.content_tag(:td, options[:surveyQuestion], :colspan => 3)
end
end
#template.content_tag(:tr) do
#template.content_tag(:th, options[:argument0])
end
end
Only the second part gets rendered:
#template.content_tag(:tr) do
#template.content_tag(:th, options[:argument0])
end
Can anyone tell me why this is?
Ruby Rails returns the last variable it used if no return is explicitly called.
( example: )
def some_method(*args)
a = 12
b = "Testing a String"
# ...
3
end # This method will return the Integer 3 because it was the last "thing" used in the method
Use an Array to return all the content_tag (WARNING: this method will return an Array, not a content_tag as you expect, you'll need to loop on it):
def itemSemanticDifferential(method, options = {})
results = []
if options[:surveyQuestion].any? then
results << #template.content_tag(:tr) do
#template.content_tag(:td, options[:surveyQuestion], :colspan => 3)
end
end
results << #template.content_tag(:tr) do
#template.content_tag(:th, options[:argument0])
end
return results # you don't actually need the return word here, but it makes it more readable
end
As asked by author of the Question, You need to loop on the result because it is an array of content_tags. Also, you need to use .html_safe to output the content_tags as HTML (not strings).
<% f.itemSemanticDifferential(:method, options = {}).each do |row| %>
<%= row.html_safe %>
<% end %>

Outputting HTML in Ruby Not working with html_safe

I have a loop which outputs some information I grabbed from a website. To make the information display in an readable fashion, I added a <br> to the string. However when it runs, it displays <br>, escaping the html. I've used html_safe and raw but neither works. What's wrong with the code? The code is called in the view home (don't worry I'll move it once the code works).
<%=
require 'rubygems'
require 'nokogiri'
require 'open-uri'
time = Time.new
month = I18n.t("date.abbr_month_names")[time.month]
day = time.day
"#{month} #{day}"
#United States
cities = [
"sfbay", "losangeles", "athensga", "phoenix", "santabarbara", "denver",
"panamacity", "miami", "austin", "bakersfield", "keys", "newyork"
]
cities.map do |city|
#Search Terms
search_terms = ["mechanic", "car", "tech"]
search_terms.map do |term|
escaped_term = CGI.escape(term)
url = "http://#{city}.craigslist.org/search/jjj?query=#{escaped_term}&catAbb=jjj&
srchType=A"
doc = Nokogiri::HTML(open(url))
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
#strings << "#{date} #{text} #{link}"
end
end
end
end
%>
Hmm not sure at all but try the following:
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
#string = "#{date} #{text} #{link}<br />"
end
end.html_safe
Another way, not solving yours but displaying with br at each line:
# in controller or whatever
#strings = []
if date = "#{month} #{day}"
#strings << "#{date} #{text} #{link}"
end
# in the view:
<%= raw(#strings.join('<br />')) %>
doc.css(".row").map do |row|
date = row.css(".itemdate").text
a_tag = row.css("a")[0]
text = a_tag.text
link = a_tag[:href]
if date = "#{month} #{day}"
"<br/>".html_safe + "#{date} #{text} #{link}"
end
end

Counting tag and text characters in an HTML document in Ruby

I need to parse an HTML document to count the number of characters for both tags (including attributes) and text in Ruby. For performance reasons, I don't want to use a DOM parser. I've looked at Nokogiri's SAX and Reader parsers and also to SaxMachine, but neither seems to offer me a way to track the parser's position in the input HTML.
Does anyone know a way to access this information in Ruby? Thanks in advance
Input string
html = <<-HTML
<html>
<head>
<title>Title</title>
</head>
<body>
Hello world!
</body>
</html>
HTML
Dumb solution
Crude solution, it counts every alphabet character (ie. </html> count for 4 characters).
tag_count = 0
text_count = 0
in_tag = false
html.each_char do |char|
case char
when '<'
in_tag = true
when '>'
in_tag = false
when /\w/
in_tag ? tag_count += 1 : text_count += 1
end
end
puts "Text char count: #{text_count}"
puts "Tag char count: #{tag_count}"
Nokogiri SAX solution
This one could be easily translated to another language (eg. Java).
require 'nokogiri'
class HtmlCounter < Nokogiri::XML::SAX::Document
attr_accessor :tag_count, :text_count, :comment_count
def initialize(filtered_tags = [])
#filtered_tags = filtered_tags
end
def start_document
#tag_count = Hash.new(0)
#text_count = Hash.new(0)
#comment_count = 0
#current_tags = []
end
def start_element(name, attrs)
# Keep track of the nesting
#current_tags.push(name)
if should_count?
# Count the end element as well
count_tag(name.length * 2)
count_tag(attrs.flatten.map(&:length).inject(0) {|sum, length| sum + length})
end
end
def end_element(name)
#current_tags.pop
end
def comment(string)
count_comment(string.length) if should_count?
end
def characters(string)
count_text(string.strip.length) if should_count?
end
def should_count?
# Are we in a filtered tag ?
(#current_tags & #filtered_tags).empty?
end
def count_text(count)
#text_count[#current_tags.last] += count
end
def count_tag(count)
#tag_count[#current_tags.last] += count
end
def count_comment(count)
#comment_count[#current_tags.last] += count
end
end
# Don't count things in title tags
counter = HtmlCounter.new(["title"])
parser = Nokogiri::HTML::SAX::Parser.new(counter)
parser.parse(html)
puts "Text char count: #{counter.text_count}"
puts "Tag char count: #{counter.tag_count}"
output :
Text char count: {"body"=>12}
Tag char count: {"html"=>8, "head"=>8, "body"=>8}
Hope this helps.