'Require' method won't work - help? - ruby-on-rails

I'm just starting to learn Ruby, and I've been following Why's (poignant) guide. At one point, I create a file titled 'wordlist.rb', within which is the following code:
code_words = {
'starmonkeys' => 'Phil and Pete, those prickly chancellors of the New Reich',
'catapult' => 'chucky go-go', 'firebomb' => 'Heat-Assisted Living',
'Nigeria' => "Ny and Jerry's Dry Cleaning (with Donuts)",
'Put the kabosh on' => 'Put the cable box on'
}
Another script calls the 'require' method on the wordlist file....
require 'wordlist'
# Get evil idea and swap in code words
print "Enter your new idea: "
idea = gets
code_words.each do |real, code|
idea.gsub!( real, code )
end
# Save the jibberish to a new file
print "File encoded. Please enter a name for this idea: "
idea_name = gets.strip
File::open( "idea-" + idea_name + ".txt", "w" ) do |f|
f << idea
end
Now, for whatever reason, I get this error when I try to run the script above:
test.rb:5: undefined local variable or method `code_words' for main:Object (NameError)
It's definitely finding and loading the wordlist.rb file (the method is returning true), but I can't seem to access the code_words Hash. Any idea what could be causing this?

code_words is a local variable inside wordlist.rb.
What you can do:
define a gloibal variable ( $wordlist)
define a constant (WORDLIST)
define another container, e.g. a class which provides the data

Related

Don't change string value on insert

I have a Model user with the following method:
def number_with_hyphen
number&.insert(8, "-")
end
When I run it several times in my tests I get the following output:
users(:default).number_with_hyphen
"340909-1234"
(byebug) users(:default).number_with_hyphen
"340909--1234"
(byebug) users(:default).number_with_hyphen
"340909---1234"
(byebug) users(:default).number_with_hyphen
"340909----1234"
It changes the number ?Here are the docs https://apidock.com/ruby/v1_9_3_392/String/insert
When I restructure my method to:
def number_with_hyphen
"#{number}".insert(8, "-") if number
end
If works like expected. The output stays the same!
How would you structure the code, how would you perform the insert?
which method should I use instead. Thanks
If you're using the insert method, which in the documentation explicitly states "modifies str", then you will need to avoid doing this twice, rendering it idempotent, or use another method that doesn't mangle data.
One way is a simple regular expression to extract the components you're interested in, ignoring any dash already present:
def number_with_hyphen
if (m = number.match(/\A(\d{8})\-?(\d+)\z/))
[ m[1], m[2] ].join('-')
else
number
end
end
That ends up being really safe. If modified to accept an argument, you can test this:
number = '123456781234'
number_with_hyphen(number)
# => "12345678-1234"
number
# => "123456781234"
number_with_hyphen(number_with_hyphen(number))
# => "12345678-1234"
number_with_hyphen('1234')
# => "1234"
Calling it twice doesn't mangle anything, and any non-conforming data is sent through as-is.
Do a clone of the string:
"#{number}".clone.insert(8, '-')

encoded URL construction fails in a Rails application

I've a very simple problem. I want to construct a encoded url from an object. I've an object called "invitation". "invitation" has following fields, "message", "date", "name". I want to construct a following encoded string from this object. So when I use this url, it will pre-fill google mail's compose form.
https://mail.google.com/mail/u/0/?view=cm&fs=1&su=Party+on+date&body=%0Amessage%0Ahttp://localhost:3000/invitations/3%0Aname%0A&tf=1
I've declared a helper as shown below (which suppose to return an encoded string and can be use in a view). ..
module ApplicationHelper
def google_mail_encoded_url(invite)
uri = URI.parse('https://mail.google.com/u/0/?')
uri.query = URI.encode_www_form('view'=> "cm", 'fs' => "1", 'su' => "Party on " + invite.date, 'body' => "\nHi,\n" + invite.message + "\n" + invitations_url(invite))
puts uri.to_s
end
end
I tried following in rails console,
#invite = Invitations.where(:id => 10)
helper.google_mail_encoded_url(#invite) #just to see the output....
This fails with NameError: undefined local variable or method `invitations_url' for main:Object. Is this the correct way to call and construct encoded url? Any help is appreciated.
That is because the routes are not loaded when you open a console.
To be able to use them run the following in your console:
> include Rails.application.reload_routes!.first.url_helpers
=> Object
> root_path
=> "/"

metaprograming String#scan and globals?

My goal is to replace methods in the String class with other methods that do additional work (this is for a research project). This works for many methods by writing code in the String class similar to
alias_method :center_OLD, :center
def center(args*)
r = self.send(*([:center_OLD] + args))
#do some work here
#return something
end
For some methods, I need to handle a Proc as well, which is no problem. However, for the scan method, invoking it has the side effect of setting special global variables from the regular expression match. As documented, these variables are local to the thread and the method.
Unfortunately, some Rails code makes calls to scan which makes use of the $& variable. That variable gets set inside my version of the scan method, but because it's local, it doesn't make it back to the original caller which uses the variable.
Does anyone know a way to work around this? Please let me know if the problem needs clarification.
If it helps at all, all the uses I've seen so far of the $& variable are inside a Proc passed to the scan function, so I can get the binding for that Proc. However, the user doesn't seem to be able to change $& at all, so I don't know how that will help much.
Current Code
class String
alias_method :scan_OLD, :scan
def scan(*args, &b)
begin
sargs = [:scan_OLD] + args
if b.class == Proc
r = self.send(*sargs, &b)
else
r = self.send(*sargs)
end
r
rescue => error
puts error.backtrace.join("\n")
end
end
end
Of course I'll do more things before returning r, but this even is problematic -- so for simplicity we'll stick with this. As a test case, consider:
"hello world".scan(/l./) { |x| puts x }
This works fine both with and without my version of scan. With the "vanilla" String class this produces the same thing as
"hello world".scan(/l./) { puts $&; }
Namely, it prints "ll" and "ld" and returns "hello world". With the modified string class it prints two blank lines (since $& was nil) and then returns "hello world". I'll be happy if we can get that working!
You cannot set $&, because it is derived from $~, the last MatchData.
However, $~ can be set and that actually does what you want.
The trick is to set it in the block binding.
The code is inspired by the old Ruby implementation of Pathname.
(The new code is in C and does not need to care about Ruby frame-local variables)
class String
alias_method :scan_OLD, :scan
def scan(*args, &block)
sargs = [:scan_OLD] + args
if block
self.send(*sargs) do |*bargs|
Thread.current[:string_scan_matchdata] = $~
eval("$~ = Thread.current[:string_scan_matchdata]", block.binding)
yield(*bargs)
end
else
self.send(*sargs)
end
end
end
The saving of the thread-local (well, actually fiber-local) variable seems unnecessary since it is only used to pass the value and the thread never reads any other value than the last one set. It probably is there to restore the original value (most likely nil, because the variable did not exist).
One way to avoid thread-locals at all is to create a setter of $~ as a lambda (but it does create a lambda for each call):
self.send(*sargs) do |*bargs|
eval("lambda { |m| $~ = m }", block.binding).call($~)
yield(*bargs)
end
With any of these, your example works!
I wrote simple code simulating the problem:
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $&; }
class String
alias_method :origin_scan, :scan
def scan *args, &b
args.unshift :origin_scan
#mutex ||= Mutex.new
begin
self.send *args do |a|
break if !block_given?
#mutex.synchronize do
p $&
case b.arity
when 0
b.call
when 1
b.call a
end
end
end
rescue => error
p error, error.backtrace.join("\n")
end
end
end
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $& }
And found the following. The change of containment of the variable $& became inside a :call function, i.e. on 3-rd step before :call $& contains a valid value, but inside the block it becomes the invalid. I guess this become due to the singularity stack and variable restoration during the change process/thread context, because, probably, :call function can't access the :scan local state.
I see two variants: the first is to avoid to use global variables in the specific function redefinitions, and second, may to dig sources of ruby more deeply.

How to make a stub return a hash value

I have a model and a method inside that defined as follows :
class Farm < ActiveRecord::Base
def flags
#has other code which returns a flag_details hash eg:
# flag_details['flag1'] = true;
# flag_details['flag2'] = false;
end
end
Now I need to write a test to verify that a particular div is displayed based on the flags that are set/unset. And I want to write a stub to be able to return these flags and then test if the page is displaying the correct div. How do I correct the following code, to get what I intend:
scenario "display div named flower when flag1 or flag2 is false" do
farm.stub(:flags).and_return("flag1" => false, "flag2" => false)
if !farm.flags['flag1'] || !farm.flags['flag2']
expect(page).to have_selector('div.flower', text: "4" )
end
end
I am a beginner in ruby, rails and rspec, so any help will be great. I also tried using the following method but it did not work:
farm.stub(:flags[]).with('flag1').and_return(false)
farm.stub(:flags[]).with('flag2').and_return(false)
I also checked this documentation (https://www.relishapp.com/rspec/rspec-mocks/v/2-4/docs/method-stubs) but did not get my answer. Any other links that could be helpful in this, are really appreciated!
Thanks!
I think you should just be able to wrap that return value in curly braces ({}) and it should solve your problem:
farm.stub(:flags).and_return({"flag1" => false, "flag2" => false})
First, get rid of the if conditional in your test. There is no reason to add control flow to a test. How stubbing works: Your test needs to call the flags method on that farm object and it will return the hash that you have specified. Have your scenario visit the page in question and then check to make sure the expectation is behaving as you have intended.

Whats the best way to put a small ruby app online?

I have a small ruby application I wrote that's an anagram searcher. It's for learning ruby, but I would like to put it up online for personal use. I have some experience with Rails, and many here have recommended Sinatra. I'm fine with either, but I cannot find any information on how to use a text file instead of a database.
The application is quite simple, validates against a text file of a word list, then finds all anagrams. I have been assuming that this should be quite simple, but I'm stuck on importing that textfile into Rails (or Sinatra if i choose that way). In the Rails project, I have placed the textfile in the lib directory.
Unfortunately, even though the path appears to be correct in Rails, I get an error:
no such file to load -- /Users/court/Sites/cvtest/lib/english.txt
(cvtest is the name of the rails project)
Here is the code. It works great by itself:
file_path = '/Users/court/Sites/anagram/dictionary/english.txt'
input_string = gets.chomp
# validate input to list
if File.foreach(file_path) {|x| break x if x.chomp == input_string}
#break down the word
word = input_string.split(//).sort
# match word
anagrams = IO.readlines(file_path).partition{
|line| line.strip!
(line.size == word.size && line.split(//).sort == word)
}[0]
#list all words except the original
anagrams.each{ |matched_word| puts matched_word unless matched_word == input_string }
#display error if
else
puts "This word cannot be found in the dictionary"
end
Factor the actual functionality (finding the anagrams) into a method. Call that method from your Web app.
In Rails, you'd create a controller action that calls that method instead of ActiveRecord. In Sinatra, you'd just create a route that calls the method. Here's a Sinatra example:
get '/word/:input'
anagrams = find_anagrams(params[:input])
anagrams.join(", ")
end
Then, when you access the http://yourapp.com/word/pool, it will print "loop, polo".
I know the question is marked as answered, but I prefer the following, as it uses query parameters rather than path based parameters, which means you can pass the parameters in using a regular GET form submission:
require 'rubygems'
require 'sinatra'
def find_anagrams word
# your anagram method here
end
get '/anagram' do
#word = params['word']
#anagrams = find_anagrams #word if #word
haml :anagram
end
And the following haml (you could use whatever template language you prefer). This will give you an input form, and show the list of anagrams if a word has been provided and an anagram list has been generated:
%h1
Enter a word
%form{:action => "anagram"}
%input{:type => "text", :name => "word"}
%input{:type => "submit"}
- if #word
%h1
Anagrams of
&= #word
- if #anagrams
%ul
- #anagrams.each do |word|
%li&= word
- else
%p No anagrams found
With sinatra, you can do anything. These examples doesn't even require sinatra, you could roll your own rack interface thing.
require 'rubygems'
require 'sinatra'
require 'yaml'
documents = YAML::load_file("your_data.yml")
Or:
require 'rubygems'
require 'sinatra'
content = Dir[File.join(__DIR__, "content/*.textile)].map {|path|
content = RedCloth(File.read(path)).to_html
}
Etcetera.

Resources