Ruby: How to import a variable from another file? - ruby

I'm trying to create a local config file to use with compass so we can cope with differing import paths on developers' machines. So far I've tried to import the file inside an exception block, incase it doesn't exist, then use the variable further down:
local_config.rb
VENV_FOLDER = 'venv'
config.rb
VENV_FOLDER = '.'
begin
require 'local_config.rb'
rescue LoadError
end
puts VENV_FOLDER
Normally I'm a Python developer so I'd expect the import to change the value of VENV_FOLDER to venv, however it's still . afterwards.
Is there a way to import local_config.rb in such a way that it overrides the value of VENV_FOLDER?

Other alternatives:
YAML (or JSON)
local_config.yml
venv_folder: 'venv'
config.rb
require 'yaml'
VENV_FOLDER = begin
YAML.load_file('local_config.yml').fetch('venv_folder')
rescue Errno::ENOENT, KeyError
'.'
end
puts VENV_FOLDER
Class instance variable
You could put the value in a class instance variable:
local_config.rb
Config.venv = 'venv'
config.rb
class Config
class << self ; attr_accessor :venv ; end
self.venv = '.'
end
begin
require './local_config.rb'
rescue LoadError
end
puts Config.venv
Constants
Also, sticking to ruby files with constants, the following is perhaps marginally clearer in its intentions and avoids having to catch exceptions.
local_config.rb
VENV_FOLDER = 'venv'
config.rb
config_file = './local_config.rb'
require config_file if File.file? config_file
VENV_FOLDER ||= '.'
puts VENV_FOLDER
All three solutions have different mechanisms for ensuring that the value will be set even if the file is missing or doesn't set the value as expected. Hope it's helpful

The path to the file is wrong. It needs to include a slash if it isn't loaded from $LOAD_PATH.
Your LoadError is being caught silently.
If you do this:
VENV_FOLDER = '.'
begin
require './local_config.rb'
rescue LoadError
end
puts VENV_FOLDER
Then you'll see it works.
Better still:
VENV_FOLDER = '.'
require File.expand_path('../local_config.rb', __FILE__) rescue LoadError
puts VENV_FOLDER
Since the second version doesn't depend on the PWD of the user who invokes the script.
Constant re-assignment is a bad idea however. Ruby will let you do it, but you'll get a warning. I believe your confusion was just with the LoadError though.

Try something like this:
local_config.rb
VENV[:folder] = 'venv'
config.rb
VENV = {:folder => '.'}
begin
load 'local_config.rb'
rescue Exception
end
puts VENV[:folder]
Here's working version for ruby 1.9.3: https://gist.github.com/1501237
for ruby 1.8.7: https://gist.github.com/1501321

Related

How to use rescue when a LoadError is raised

I'm trying to implement a file dialog using Tk. This aspect has worked but my error checking isn't working.
Since this file dialog can only take certain extensions I made it raise a LoadError, but I also don't want the program to stop, I want it to reopen to allow the user to pick another file.
Each way I've tried has only ended in an infinite loop or a LoadError stopping the program.
My code is:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
def self.fileDialog
TkClipboard.append(Tk.getOpenFile)
f = TkClipboard.get
begin
unless extenstionCheck(f)
raise LoadError, 'Please select a valid file type'
end
rescue LoadError
fileDialog
end
end
def self.extenstionCheck(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
true if filetypes.include?(file)
end
end
There's no need to use TkClipboard, nor to use an exception.
Did misspelling the word, 'extension' blind you to your nearby error of checking whether filetypes included file, instead of type?
Your program, minimally changed as follows, works for me:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
def self.fileDialog
while true
f = Tk.getOpenFile
break if extension_okay?(f)
Tk.messageBox message: 'Please select a valid file type!', detail: "Selection was: #{f}"
end
f
end
def self.extension_okay?(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
filetypes.include?(type)
end
end
p FileExplorer.fileDialog
This is completely inappropriate (and unnecessary) use of LoadError.
Raised when a file required (a Ruby script, extension library, …)
fails to load.
Its a low level error that does not inherit from StandardError and is tied to Kernel#require.
Instead declare your own exceptions in your own namespace:
module FileExplorer
require 'tk'
require 'tkextlib/tile'
FileTypeError = Class.new(::StandardError)
def self.fileDialog
TkClipboard.append(Tk.getOpenFile)
f = TkClipboard.get
begin
unless extenstionCheck(f)
raise FileTypeError, 'Please select a valid file type'
end
rescue FileTypeError
fileDialog
end
end
def self.extenstionCheck(file)
filetypes = ['.xlsx', '.xls', '.csv', '.xml']
type = File.extname(file)
true if filetypes.include?(file)
end
end

How do I reference a method in a different class from a method in another class?

I have a module and class in a file lib/crawler/page-crawler.rb that looks like this:
require 'oga'
require 'net/http'
require 'pry'
module YPCrawler
class PageCrawler
attr_accessor :url
def initialize(url)
#url = url
end
def get_page_listings
body = Net::HTTP.get(URI.parse(#url))
document = Oga.parse_html(body)
document.css('div.result')
end
newpage = PageCrawler.new "http://www.someurl"
#listings = newpage.get_page_listings
#listings.each do |listing|
bizname = YPCrawler::ListingCrawler.new listing['id']
end
end
end
Then I have another module & class in another file lib/crawler/listing-crawler.rb that looks like this:
require 'oga'
require 'pry'
module YPCrawler
class ListingCrawler
def initialize(id)
#id = id
end
def extract_busines_name
binding.pry
end
end
end
However, when I try to run this script ruby lib/yp-crawler.rb which executes the page-crawler.rb file above and works without the YPCrawler call, I get this error:
/lib/crawler/page-crawler.rb:23:in `block in <class:PageCrawler>': uninitialized constant YPCrawler::ListingCrawler (NameError)
The issue is on this line:
bizname = YPCrawler::ListingCrawler.new listing['id']
So how do I call that other from within my iterator in my page-crawler.rb?
Edit 1
When I just do `ListingCrawler.new listing['id'], I get the following error:
uninitialized constant YPCrawler::PageCrawler::ListingCrawler (NameError)
Edit 2
Here is the directory structure of my project:
Edit 3
My yp-crawler.rb looks like this:
require_relative "yp-crawler/version"
require_relative "crawler/page-crawler"
require_relative "crawler/listing-crawler"
module YPCrawler
end
In your yp-crawler.rb file, based on the structure that you posted, you should have something like:
require 'yp-crawler/version'
require 'crawler/listing-crawler'
require 'crawler/page-crawler'
Try this, in your yp-crawler.rb add the line:
Dir["#{File.dirname(__FILE__)}/crawler/**/*.rb"].each { |file| load(file) }
That should automatically include all files in your /crawler directory at runtime. Might want to do the same for the other directories.
Let me know if that helps :)

Three Ruby classes, more than three problems?

I have three Ruby files in the same directory:
classthree.rb
otherclass.rb
samplecode.rb
Here are the contents of classthree.rb:
require './samplecode.rb'
require './otherclass.rb'
class ClassThree
def initialize()
puts "this class three here"
end
end
Here are the contents of samplecode.rb:
require './otherclass.rb'
require './classthree.rb'
class SampleCode
$smart = SampleCode.new
#sides = 3
##x = "333"
def ugly()
g = ClassThree.new
puts g
puts "monkey see"
end
def self.ugly()
s = SampleCode.new
s.ugly
puts s
puts $smart
puts "monkey see this self"
end
SampleCode.ugly
end
Here are the contents of otherclass.rb:
require './samplecode.rb'
require './classthree.rb'
END {
puts "ending"
}
BEGIN{
puts "beginning"
}
class OtherClass
def initialize()
s = SampleCode.new
s.ugly
end
end
My two questions are:
There has to be a better way than require './xyz.rb' for every class in the directory. Isn't there something like require './*.rb'?
When I run ruby otherclass.rb I get the following output:
Why do I get "beginning" and "ending" twice each??
At 1 - The best way to deal with it is to create another file. You can call it environment.rb or initialize.rb, and it would require all the needed files.
$LOAD_PATH.unshift File.dirname(__FILE__)
require 'samplecode.rb'
require 'classthree.rb'
require 'classthree.rb'
Now you only need to require this file once on the start of the application.
At 2 - You started from file 'otherclass.rb'. It displays the first 'beginning' bit and then it loads samplecode.rb file. At this point, 'otherclass.rb' has not been loaded yet - it was not required by any other file. hence samplecode.rb is rerunning whole otherclass.rb, which is being required there. Rerunning doesn't reload 'samplecode.rb' as it was already required (require checks first whether file was or was not required). That's why you're seeing those messages twice.

How do I monkey-patch ruby's URI.parse method

Some popular blog sites typically use square brackets in their URLs but ruby's built-in URI.parse() method chokes on them, raising a nasty exception, as per:
http://redmine.ruby-lang.org/issues/show/1466
I'm trying to write a simple monkey-patch that gracefully handles URLs with the square bracket. The following is what I have so far:
require 'uri'
module URI
def self.parse_with_safety(uri)
safe_uri = uri.replace('[', '%5B')
safe_uri = safe_uri.replace(']', '%5D')
URI.parse_without_safety(safe_uri)
end
alias_method_chain :parse, :safety
end
But when run, this generates an error:
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.8/lib/active_support/core_ext/module/aliasing.rb:33:in alias_method: NameError: undefined method 'parse' for module 'URI'
How can I successfully monkey-patch URI.parse?
alias_method_chain is executed on the module level so it only affects instance methods.
What you have to do is execute it on the module's class level:
require 'uri'
module URI
class << self
def parse_with_safety(uri)
parse_without_safety uri.gsub('[', '%5B').gsub(']', '%5D')
end
alias parse_without_safety parse
alias parse parse_with_safety
end
end
#nil his comment is very helpful, we ended up with the following:
def parse_with_safety(uri)
begin
parse_without_safety uri.gsub(/([{}|\^\[\]\#`])/) {|s| URI.escape(s)}
rescue
parse_without_safety '/'
end
end

cattr_accessor outside of rails

I'm trying to use the google_search ruby library (code follows) but it complains that 'cattr_accessor is an undefined method' - any ideas why this might be or how I could fix it?
require 'rubygems'
require 'google_search'
GoogleSearch.web :q => "pink floyd"
cattr_accessor seems to be a Rails extension that acts like attr_accessor, but is accessible on both the class and its instances.
If you want to copy the source of the cattr_accessor method, check out this documentation:
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 46
def cattr_accessor(*syms)
cattr_reader(*syms)
cattr_writer(*syms)
end
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 4
def cattr_reader(*syms)
syms.flatten.each do |sym|
next if sym.is_a?(Hash)
class_eval("unless defined? ##\#{sym}\n##\#{sym} = nil\nend\n\ndef self.\#{sym}\n##\#{sym}\nend\n\ndef \#{sym}\n##\#{sym}\nend\n", __FILE__, __LINE__)
end
end
# File vendor/rails/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb, line 24
def cattr_writer(*syms)
options = syms.extract_options!
syms.flatten.each do |sym|
class_eval("unless defined? ##\#{sym}\n##\#{sym} = nil\nend\n\ndef self.\#{sym}=(obj)\n##\#{sym} = obj\nend\n\n\#{\"\ndef \#{sym}=(obj)\n##\#{sym} = obj\nend\n\" unless options[:instance_writer] == false }\n", __FILE__, __LINE__)
end
end
You can get this functionality by including the Ruby Facets gem. Reference the source here:
https://github.com/rubyworks/facets/blob/master/lib/core/facets/cattr.rb
You generally don't need to require all code from the gem. You can selectively require what you want. There are quite a few useful extensions in the gem though.

Resources