How can I read Bytes from a File in Ruby 1.9? - ruby

In Ruby 1.9 the File and IO libraries were changed -- they now seem to always interpret the data as encoded strings (e.g. UTF-8), and the returned values seem to be always strings.
I need to read a file in Ruby 1.9 byte by byte, without any modification or interpretation of the data.
I want to read byte sequences, not encoded strings.
Any tips on how to best do this?

I had a similar problem in a gem I wrote. Here's the relevant code:
(you don't need the require statements)
# ==============================================================================
# Loading Libraries and Stuff needed for Ruby 1.9 vs 1.8 Compatibility
# ==============================================================================
# the idea here is to define a couple of go-between methods for different classes
# which are differently defined depending on which Ruby version it is -- thereby
# abstracting from the particular Ruby version's API of those classes
if RUBY_VERSION >= "1.9.0"
require "digest/md5"
require "digest/sha1"
include Digest
require 'fileutils' # replaces ftools
include FileUtils::Verbose
class File
def read_bytes(n) # returns a string containing bytes
self.bytes.take(n)
end
def write_bytes(bytes)
self.syswrite(bytes)
end
def get_byte
self.getbyte # returns a number 0..255
end
end
ZEROBYTE = "\x00".force_encoding(Encoding::BINARY) unless defined? ZEROBYTE
else # older Ruby versions:
require 'rubygems'
require "md5"
require "sha1"
require 'ftools'
def move(a,b)
File.move(a,b)
end
class String
def getbyte(x) # when accessing a string and selecting x-th byte to do calculations , as defined in Ruby 1.9
self[x] # returns an integer
end
end
class File
def read_bytes(n)
self.read(n) # should use sysread here as well?
end
def write_bytes(bytes)
self.write(bytes) # should use syswrite here as well?
end
def get_byte # in older Ruby versions <1.9 getc returned a byte, e.g. a number 0..255
self.getc # returns a number 0..255
end
end
ZEROBYTE = "\0" unless defined? ZEROBYTE
end

IO has the binmode method (calling it is setting it), which disables newline and encoding conversion. The File class inherits this method.

Related

Upgade to Ruby 3.1 breaks code when using CSV class from standard library

I'm upgrading a Project written for JRuby 1.7 (corresponding on the language level to Ruby 1.9) to JRuby 9.4 (corresponding to Ruby 3.1.0). In this code, we have
require 'csv'
....
CSV.parse(string, csv_options) { .... }
where string is of class String and csv_optionsis of class Hash. This statement produces, when run under the new Ruby version, the error
ArgumentError:
wrong number of arguments (given 2, expected 1)
I found in the Ruby docs the following difference in the definition of parse:
Old version:
def parse(str, options={}, &block)
New version
def parse(str, **options, &block)
I understand that in the new Ruby, I would have to invoke parse as
CSV.parse(string, **csv_options) {....}
However, I would like to keep the code compatible for both versions, at least for some transition period, but the old JRuby does not understand **variable (I would get a syntax error, unexpected tPOW).
Is there a way to write the invocation of CSV.parse in such a way, that it preserves the original semantics and can run under Ruby 1.9 and Ruby 3.1? Currently the best solution for this problem which I can think of, is to write something like turning the block argument into a proc and writing
if RUBY_VERSION < '2'
CSV.parse(string, csv_options, &myproc)
else
# Without the eval, the compiler would complain about
# the ** when compiled with Ruby 1.9
eval "CSV.parse(string, **csv_options, &myproc)"
end
which looks pretty awful.
Not sure exactly what you are passing as csv_options but all versions can handle this using an a combination of implicit Hash/kwargs. e.g.
CSV.parse(string, col_sep: '|', write_headers: false, quote_empty: true) { ... }
If this is not an option then you going to need to patch the CSV class to make it backwards compatible e.g.
csv_shim.rb
# Shim CSV::parse to mimic old method signature
# while supporting Ruby 3 kwargs argument passing
module CSVShim
def parse(string, options={}, &block)
super(string, **options, &block)
end
end
CSV.singleton_class.prepend(CSVShim)
Then you can modify as:
require 'csv'
require 'csv_shim.rb' if RUBY_VERSION > '2.6.0'
#...
CSV.parse(string, csv_options) { .... }

Ruby gem - How do I test my own ruby gem via IRB?

Okay so I am completely new to using IRB so I have no idea what I am doing wrong.
Here is my gem that I built
# frozen_string_literal: true
require_relative "badwordgem/version"
require 'yaml' #this will need to use the badlist.yml file
module Badwordgem
class Error < StandardError; end
class Base
class << self # assign class to itself so self does not need to be used for every method
def badlist
#badlist ||= YAML.load_file(File.expand_path("badlist.yml", __dir__)) # This will load in the bad words from our YML file and it will assign it to the badlist variable
end
def sanitize(input = "sassy") # method to take in user input to check for profanity
word = input.downcase # It will change the user input and set it to lowercase to match our bad list of words
badlist.each do |key, value| # For every word in the badlist assign it a key(a bad word), and a value(then replace the bad work with * symbol)
word.gsub!(/\b#{key}\b/, value) # For each word the user has inputed replace the old word(key) with the new word(value)
end
word # return the word whether it contains profanity or not
end
end
end
end
Essentially it tests to see if a word is bad or not based on my list of badwords.
I have installed and built it but I have no idea how to test it in IRB.
I've tried running badwordgem.sanitize(hello) etc. but that clearly is wrong as it gives me this error undefined local variable or method badwordgem' for main:Object'
What command in IRB do I type to test it??

IO streams inconsistencies in different versions of ruby

I am trying to create a way to mirror all three standard (input,out and error) streams of a ruby program to different files.
I was able to do that for output stdout and error stderr streams.
However there is inconsistencies between different ruby version on how they handle IO streams.
Bottom line is
How do I handle stream duplication in older versions of ruby (point 2) ?
Issue Summary
Older ruby versions don't support variadic arguments while new version does, that can be handled.
On same older versions, even if I try to work around it by join ing arguments, stream contents are duplicated.
Variadic arguments issue
In ruby 2.1.x and ruby 2.2.x, $stdout accepts only single arguments.
So for those version of ruby
$stdout.write("Test") # works
while this doesn't work
$stdout.write("Hello","Test")
ArgumentError: wrong number of arguments (2 for 1)
from (pry):6:in `write'
However for ruby version 2.5.x and 2.6.x variable parameters works
$stdout.write("Hello")
$stdout.write("This","works", "as", "well")
Duplication of stream content, if I add version specific code to handle stream parameters, older ruby version end up duplicating the stream content.
Older ruby version
driver.rb:4 in `puts` --> This is it driver.rb:4 in `puts` -->
while on
New ruby versions
driver.rb:4 in `puts` --> This is it
Here is the code
reox.rb
require_relative 'reox_io.rb'
require_relative 'reox_file.rb'
class Reox
DEFAULT_RECORDING_PATH = "recordings"
def initialize(recording_path)
#recording_paths = recording_path || DEFAULT_RECORDING_PATH
end
def self.record
input_log,output_log,error_log = setup_files
$stdout = ReoxIO.new(STDOUT,output_log)
$stderr = ReoxIO.new(STDERR,error_log)
yield
end
private
def self.setup_files
["input.log","output.log","error.log"].map do |file_name|
self.setup_file(file_name)
end
end
def self.setup_file(file_name)
file = ReoxFile.open(File.join(__dir__,file_name),"a")
file.sync = true
file
end
end
reox_file.rb
class ReoxFile < File
end
reox_io.rb
# Insipired from https://stackoverflow.com/a/6407200
require_relative 'reox_file.rb'
class ReoxIO
$LOG_SEPARATOR = " --> ".freeze
def initialize(*streams)
#streams = streams
#verbose = true
end
def write(*content)
#streams.each do |stream|
if #verbose and stream.is_a? ReoxFile
stream.write([caller[2],$LOG_SEPARATOR,*content].join)
else
stream.write(*content)
end
end
end
def flush
#streams.each(&:flush)
end
def close
#streams.each(&:close)
end
end
driver.rb
require_relative 'reox.rb'
Reox.record do
puts "This is it"
end

Display all constants in 'Digest'

I would like to know how to see every constant available on the Digest module in advance programmatically. The behaviour seen below is due to const_missing used here:
require 'digest'
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base]
Digest::MD5
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base, :MD5]
Digest::SHA1
Digest.constants
#=> [:Class, :REQUIRE_MUTEX, :Instance, :Base, :MD5, :SHA1]
Given that they are metaprogramming in possible digests, how can I know all possible available digests?
For knowledge, the ones that appear to be available in Ruby 2.4.1 should be [:SHA256, :SHA384, :SHA512, :SHA1, :SHA2, :MD5, :RMD160]
Here's a code snipped from the current master branch of ruby:
module Digest
def self.const_missing(name) # :nodoc:
case name
when :SHA256, :SHA384, :SHA512
lib = 'digest/sha2.so'
else
lib = File.join('digest', name.to_s.downcase)
end
begin
require lib
rescue LoadError
raise LoadError, "library not found for class Digest::#{name} -- #{lib}", caller(1)
end
unless Digest.const_defined?(name)
raise NameError, "uninitialized constant Digest::#{name}", caller(1)
end
Digest.const_get(name)
end
# ...
end
...So, you can't really list all constants without knowing them already! You need to either require the necessary file, or reference the constant directly (which will load the file dynamically, as seen above).
Any workaround solution I could give would only be guaranteed to work for a specific version of ruby. You'd be better off to just read the documentation and load each library explicitly, unfortunately!

How can I mimic Node.js's require function in Ruby?

In node.js you can write:
var lib = require('lib');
but in Ruby the require function simply runs the code in the file and true is returned.
Currently, I'm using a very dirty solution:
main.rb:
$stuff = []
require './file1.rb'
require './file2.rb'
# and so on
file1.rb:
$stuff << something
and so on.
How can I eliminate the use of a global variable?
eg:
main.rb:
$stuff = []
$stuff << cool_require './file1.rb'
# etc
file1.rb:
exports.what = something
One of the biggest errors when working with a language, is trying to make the language working like a different one.
Ruby is not NodeJs, there are features built-in into each language that are unique to the language and cannot be reproduced easily.
In other words, there is no way to implement the NodeJS require behavior in Ruby because in Ruby there is no notion of export. When you require a file, every method/class included in the required file are made available to the scope.
In Ruby there are objects and method visibility. The way you have to make a method visible or not is to declare it as public or private/protected.
Well, first consider that Ruby is not Node.js. As Simone Carletti said, there are some features that are unique to each language. Sometimes it's good to take from other language but sometimes it's bad.
There are few things that you must keep in mind:
meth is method invocation, to pass method you use method method: method(:meth) or package it into module/class
you can use class/module by assigning it to some 2nd variable:
class A;
def self.aa; puts 'aa'; end;
end;
New_a = A;
New_a.aa # aa;
eval is dangerous method(you can evaluate unknown code)
Method:
Here is one way you can do. It is not idiot-proof tough. It is not 100% safe(eval). :
file1.rb:
Module.new do
def self.meth1
42
end
def self.meth2
'meth2'
end
end
This file contain module with 2 methods.
I am using Module.new because it returns object that you want. You can assign it later into variable/constant.
I am using self.meth* so you don't have to include but run instantly it like this: module_name.meth1()
req.rb:
def cool_require name
eval(File.read name)
end
Some_variable = cool_require('req.rb')
puts Some_variable.meth1 # 42
puts Some_variable.meth2 # meth2
cool_require reads filename(argument name) and evaluate it(it is just like you would type it in irb/pry)
Some_variable is constant. It won't disappear that easily.
2 last line is how it works. As fair I remember, that's how node.js' require works.

Resources