Undefined method for string - ruby-on-rails

I don't understand why I can't call the method colorize from color.rb.
I wrote include color inside the class, but when I try to execute the script I get this error in wget:
undefined method `colorize' for #<String:0x00000001152d30>
This is the code:
$LOAD_PATH << './lib'
require 'color'
class Download
include color
def wget(arr)
FileUtils.cd('/mnt/list')
site = "xxxxx"
arr.each do |f|
wget = system("wget #{site}#{f}")
logger.info("wget: #{f}".colorize("blue"))
end
end
end
The file color.rb with the method colorize
module Color
def colorize(color, options = {})
background = options[:background] || options[:bg] || false
style = options[:style]
offsets = ["gray","red", "green", "yellow", "blue", "magenta", "cyan","white"]
styles = ["normal","bold","dark","italic","underline","xx","xx","underline","xx","strikethrough"]
start = background ? 40 : 30
color_code = start + (offsets.index(color) || 8)
style_code = styles.index(style) || 0
"\e[#{style_code};#{color_code}m#{self}\e[0m"
end
end

As soon as you want to call colorize method on Strings instance, you should monkeypatch the String class:
class String
include Color
end
include color string in your Download class is senseless.
The snippet might be put anywhere in your code, e. g. right after the Color module definition. Since you have String class monkeypatched as shown above, you yield an ability to call colorize on string instances. The summing up:
module Color
def colorize(color, options = {})
....
end
end
class String
include Color
end
puts 'a'.colorize(...) # ⇒ works

Related

i18n-tasks custom scanner for enums

I would like to create a custom scanner for i18n-tasks that can detect enums declared as hashes in models.
My enum declaration pattern will always be like this:
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1}, _prefix: true
enum subject: { science: 0, literature: 1, music: 2, art: 3 }, _prefix: true
end
The enums will always be declared as hashes, and will always have a numerical hash value, and will always have the option _prefix: true at the end of the declaration. There can be any number of values in the hash.
My custom scanner currently looks like this:
require 'i18n/tasks/scanners/file_scanner'
class ScanModelEnums < I18n::Tasks::Scanners::FileScanner
include I18n::Tasks::Scanners::OccurrenceFromPosition
# #return [Array<[absolute key, Results::Occurrence]>]
def scan_file(path)
text = read_file(path)
text.scan(/enum\s([a-zA-Z]*?):\s\{.*\W(\w+):.*\}, _prefix: true$/).map do |prefix, attribute|
occurrence = occurrence_from_position(
path, text, Regexp.last_match.offset(0).first)
model = File.basename(path, ".rb") #.split('/').last
name = prefix + "_" + attribute
["activerecord.attributes.%s.%s" % [model, name], occurrence]
end
end
end
I18n::Tasks.add_scanner 'ScanModelEnums'
However this is only returning the very last element of each hash:
activerecord.attributes.conversation.status_archived
activerecord.attributes.conversation.subject_art
How can I return all the elements of each hash? I am wanting to see a result like this:
activerecord.attributes.conversation.status_active
activerecord.attributes.conversation.status_archived
activerecord.attributes.conversation.subject_science
activerecord.attributes.conversation.subject_literature
activerecord.attributes.conversation.subject_music
activerecord.attributes.conversation.subject_art
For reference, the i18n-tasks github repo offers an example of a custom scanner.
The file scanner class that it uses can be found here.
This works:
def scan_file(path)
result = []
text = read_file(path)
text.scan(/enum\s([a-zA-Z]*?):\s\{(.*)}, _prefix: true$/).each do |prefix, body|
occurrence = occurrence_from_position(path, text,
Regexp.last_match.offset(0).first)
body.scan(/(\w+):/).flatten.each do |attr|
model = File.basename(path, ".rb")
name = "#{prefix}_#{attr}"
result << ["activerecord.attributes.#{model}.#{name}", occurrence]
end
end
result
end
It's similar to your 'answer' approach, but uses the regex to get all the contents between '{...}', and then uses another regex to grab each enum key name.
The probable reason your 'answer' version raises an error is that it is actually returning a three-dimensional array, not two:
The outer .map is an array of all iterations.
Each iteration returns retval, which is an array.
Each element of retail is an array of ['key', occurrence] pairs.
This isn't the answer, this is just the other attempt I made, which outputs a two dimensional array instead of a single array:
require 'i18n/tasks/scanners/file_scanner'
class ScanModelEnums < I18n::Tasks::Scanners::FileScanner
include I18n::Tasks::Scanners::OccurrenceFromPosition
# #return [Array<[absolute key, Results::Occurrence]>]
def scan_file(path)
text = read_file(path)
text.scan(/enum\s([a-zA-Z]*?):\s\{(.*)\}, _prefix: true/).map do |prefix, attributes|
retval = []
model = File.basename(path, ".rb")
names = attributes.split(",").map!{ |e| e.strip; e.split(":").first.strip }
names.each do |attribute|
pos = (Regexp.last_match.offset(0).first + 8 + prefix.length + attributes.index(attribute))
occurrence = occurrence_from_position(
path, text, pos)
name = prefix + "_" + attribute
# p "================"
# p type
# p message
# p ["activerecord.attributes.%s.%s" % [model, name], occurrence]
# p "================"
retval.push(["activerecord.attributes.%s.%s" % [model, name], occurrence])
end
retval
end
end
end
I18n::Tasks.add_scanner 'ScanModelEnums'
This however gives me an error for the second detected attribute:
gems/i18n-tasks-0.9.34/lib/i18n/tasks/scanners/results/key_occurrences.rb:48:in `each': undefined method `path' for ["activerecord.attributes.conversation.status_archived", Occurrence(app/models/project.rb:3:32:98::)]:Array (NoMethodError)

Passing variables between classes in Ruby on Rails

So I have two files, one called a.rb and one called b.rb. Here's the contents in both:
# a.rb
class A
def initialize
#variable = ""
#module_b = B.new(self)
end
def pass_to_b(self)
#module_b.do_something(#variable)
end
def set_variable(var)
# var = empty
#variable = var
end
end
and
# b.rb
class B
def initialize(module_a)
#module_a = module_a
end
def set_variable_in_a(data)
#module_a.set_variable(data)
end
def do_something(variable)
# variable = empty
set_variable_in_a("hello world")
end
end
This is just an example of what I'm dealing with. If I'm trying to start a function in Class A, which is supposed to do something in ClassB and then change an instance variable in Class A, I'm not sure how to do this properly. This is what I've tried, however:
a = A.new
a.pass_to_b
Class B cannot see the instance variable #variable, and if it tries to set_variable_in_a, that doesn't work either. It's like the do_something function in Class A successfully calls the do_something function in Class B, but the instance variable information is not available. I thought by passing self to Class B, we'd be able to at least call the function
My MRI throws exeption about
def pass_to_b(self)
because you can't pass self to method as argument.
You need delete 'self' how argument
Run code below and you will see that #variable of instance of Class A has '123hello world' string
class A
def initialize
#variable = "123"
#module_b = B.new(self)
end
def pass_to_b
#module_b.do_something(#variable)
end
def set_variable(var)
# var = empty
#variable = var
end
end
# b.rb
class B
def initialize(module_a)
#module_a = module_a
end
def set_variable_in_a(data)
#module_a.set_variable(data)
end
def do_something(variable)
set_variable_in_a(variable + "hello world")
end
end
a = A.new
a.pass_to_b
display variable 'a' and you will see something like this
#<A:0x00007fdaba0f3c90 #variable="123hello world", #module_b=#<B:0x00007fdaba0f3c40 #module_a=#<A:0x00007fdaba0f3c90 ...>>>

Return members of a hashmap through a class get method

The following returns the default "client?":
class ClientMap
def initialize
##clients = {"DP000459": "BP"}
##clients.default = "client?"
end
def get(id)
return ##clients[:id]
end
end
clientMap = ClientMap.new
cKey = "DP000459"
puts clientMap.get(cKey)
Could anybody explain why I cannot retrieve anything but the 'default'?
You've got two problems. First, you are using the symbol syntax in your hash, which works only if your keys are symbols. If you want keys to be strings, you need to use hash-rocket syntax: ##clients = {'DP000459' => 'BP'}.
Second, your method returns clients[:id] regardless of what parameter is provided. The key is the symbol :id rather than the local variable id. You need to change this to ##clients[id].
Here's a cleaned-up version of what you want:
class ClientMap
def initialize
##clients = {'DP000459' => 'BP'}
##clients.default = 'client?'
end
def get(id)
##clients[id]
end
end
I've also taken the liberty of making the spacing more Ruby-idiomatic.
Finally, for variable names in Ruby, use snake_case:
>> client_map = ClientMap.new
>> c_key = 'DP000459'
>> client_map.get(c_key)
#> "BP"
Look at these code:
h = { foo: 'bar' } # => {:foo=>"bar"}
h.default = 'some default value' # => "some default value"
h[:foo] # => "bar"
h[:non_existing_key] # => "some default value"
You can read here about Hash#default method
Returns the default value, the value that would be returned by hsh if
key did not exist in hsh

Class variable access from top level

I am a complete beginner to Ruby. I am working on Lesson 45 of Learn Ruby the Hard Way currently and am creating a game similar to Zork and Adventure.
I have created a structure where I am creating 'scenes' in different files and requiring all the scenes in one file where I have an engine/map that ensures if the current scene does not equal 'finished' that it runs 'X' scene's 'enter' method.
However I have two issues:
1) I keep getting a error saying 'Warning class variable access from top level'
2) Even though the script is running I get
ex45.rb:30:in `play': undefined method `enter' for nil:NilClass (NoMethodError) from ex45.rb:59:in
The following is all of my code from each file. My apologies if it's a long read, but I would love to know why I am getting these two errors and what I can do to fix them.
Ex45.rb:
require "./scene_one.rb"
require "./scene_two.rb"
require "./scene_three.rb"
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
current_scene.enter()
end
end
class Map
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
a_map = Map.new('scene_one')
a_game = Engine.new(a_map)
a_game.play()
scene_one.rb:
class SceneOne
def enter
puts "What is 1 + 2?"
print "> "
answer = $stdin.gets.chomp
if answer == "3"
puts "Good job"
return 'scene_two'
else
puts "try again"
test
end
end
end
scene_two.rb
class SceneTwo
def enter
puts "1 + 3?"
print "> "
action = $stdin.gets.chomp
if action == "4"
return 'scene_three'
else
puts "CANNOT COMPUTE"
end
end
end
scene_three.rb
class SceneThree
def enter
puts "This is scene three"
end
end
Thanks in advance!
Answer to your first question:
You need to move the class variable definitions inside your Map class to get rid of these warnings:
Ex45.rb:5: warning: class variable access from toplevel
Ex45.rb:6: warning: class variable access from toplevel
Ex45.rb:7: warning: class variable access from toplevel
So, your Map class would look like this:
class Map
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
To answer your 2nd question:
You are getting undefined method 'enter' for nil:NilClass (NoMethodError) because your current_scene becomes nil at some point and then you try to call: current_scene.enter() i.e. nil.enter and it fails with that error message.
To solve this problem, you have to make sure you always have some value in your current_scene i.e. make sure it's not nil.
I think, you can just remove current_scene.enter() line from the end of your play method in the Engine class. So, your Engine class will look like this:
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
# current_scene.enter()
end
end
And, you won't get that error anymore.
Just so you know:
##y = 20
p Object.class_variables
--output:--
1.rb:1: warning: class variable access from toplevel
[:##y]
And:
class Object
def self.y
##y
end
end
puts Object.y
--output:--
20
But:
class Dog
##y = "hello"
def self.y
##y
end
end
puts Dog.y #=>hello
puts Object.y #=>What do you think?
The output of the last line is the reason that class variables are not used in ruby. Instead of class variables, you should use what are known as class instance variables:
class Object
#y = 10 #class instance variable
def self.y
#y
end
end
puts Object.y
class Dog
#y = "hello"
def self.y
#y
end
end
puts Dog.y #=> hello
puts Object.y #=> 10
A class instance variable is just an #variable that is inside the class, but outside any def. And instead of there being one ##variable that is shared by all the subclasses, each subclass will have its own #variable.

Ruby/Rails: Determine variables from plain text to update via form

I'm creating an app where users can edit their own CSS (in SCSS syntax). That works fine, however, I eventually want these CSS files to be "programmable" so that users that don't know CSS can still edit them in a basic manner. How?
If I can mark certain things as editable, I don't have to make an impossible database schema. For example I have a scss file called style.scss:
// #type color
$header_bg_color: #555;
// #type image
$header_image: "http://someurl.com/image.jpg";
Then I can do this:
SomeParser.parse(contents of style.scss here)
This will return a hash or something similar of variables:
{:header_bg_color => {:type => "color", :value => "#555"}, :header_image => {:type => "image", :value => "http://someurl.com/image.jpg"} }
I can use the above hash to create a form which the novice user can use to change the data and submit. I believe I know how to do the GET and POST part.
What would be the best way to create / configure my own parser so that I could read the comments and extract the "variables" from this? And then, update the text file easily again?
Another possible way is something like this:
o = SomeParser.new(contents of style.scss here)
o.header_bg_color #returns "#555"
o.header_image = "http://anotherurl.com/image2.jpg" # "updates" or replaces the old header image variable with the new one
o.render # returns the text with the new values
Thanks in advance!
I haven't used it thoroughly, but my tests pass. I think it's enough to get the idea :) It took me several hours of study, then several more to implement it.
Btw, I did not do any optimization here. For me, it doesn't need to be quick
Look at my spec file:
require 'spec_helper'
describe StyleParser do
describe "given properly formatted input" do
it "should set and return variables properly" do
text = %{# #name Masthead Background Image
# #kind file
# #description Background image.
$mbc2: "http://someurl.com/image.jpg";
# #name Masthead BG Color
# #kind color
# #description Background color.
$mbc: #555;}
#s = StyleParser.new(text)
#s.mbc.name.should == "Masthead BG Color"
#s.mbc.kind.should == "color"
#s.mbc.description.should == "Background color."
#s.mbc.value.should == "#555"
#s.mbc2.name.should == "Masthead Background Image"
#s.mbc2.kind.should == "file"
#s.mbc2.description.should == "Background image."
#s.mbc2.value.should == %Q("http://someurl.com/image.jpg")
end
end
describe "when assigning values" do
it "should update its values" do
text = %{# #name Masthead Background Image
# #kind file
# #description Background image.
$mbc2: "http://someurl.com/image.jpg";}
#s = StyleParser.new(text)
#s.mbc2.value = %Q("Another URL")
#s.mbc2.value.should == %Q("Another URL")
rendered_text = #s.render
rendered_text.should_not match(/http:\/\/someurl\.com\/image\.jpg/)
rendered_text.should match(/\$mbc2: "Another URL";/)
#s.mbc2.value = %Q("Some third URL")
#s.mbc2.value.should == %Q("Some third URL")
rendered_text = #s.render
rendered_text.should_not match(/\$mbc2: "Another URL";/)
rendered_text.should match(/\$mbc2: "Some third URL";/)
end
it "should render the correct values" do
text_old = %{# #name Masthead Background Image
# #kind file
# #description Background image.
$mbc2: "http://someurl.com/image.jpg";}
text_new = %{# #name Masthead Background Image
# #kind file
# #description Background image.
$mbc2: "Another URL";}
#s = StyleParser.new(text_old)
#s.mbc2.value = %Q("Another URL")
#s.render.should == text_new
end
end
end
Then the following 2 files:
# Used to parse through an scss stylesheet to make editing of that stylesheet simpler
# Ex. Given a file called style.scss
#
# // #name Masthead Background Color
# // #type color
# // #description Background color of the masthead.
# $masthead_bg_color: #444;
#
# sp = StyleParser.new(contents of style.scss)
#
# # Reading
# sp.masthead_bg_color.value # returns "#444"
# sp.masthead_bg_color.name # returns "Masthead Background Color"
# sp.masthead_bg_color.type # returns "color"
# sp.masthead_bg_color.description # returns "Background color of the masthead."
#
# # Writing
# sp.masthead_bg_color.value = "#555"
# sp.render # returns all the text above except masthead_bg_color is now #555;
class StyleParser
def initialize(text)
#text = text
#variables = {}
#eol = '\n'
#context_lines = 3
#context = "((?:.*#{#eol}){#{#context_lines}})"
end
# Works this way: http://rubular.com/r/jWSYvfVrjj
# Derived from http://stackoverflow.com/questions/2760759/ruby-equivalent-to-grep-c-5-to-get-context-of-lines-around-the-match
def get_context(s)
regexp = /.*\${1}#{s}:.*;[#{#eol}]*/
#text =~ /^#{#context}(#{regexp})/
before, match = $1, $2
"#{before}#{match}"
end
def render
#variables.each do |key, var|
#text.gsub!(/^\$#{key}: .+;/, %Q($#{key}: #{var.value};))
end
#text
end
def method_missing(method_name)
if method_name.to_s =~ /[\w]+/
context = get_context(method_name)
#variables[method_name] ||= StyleVariable.new(method_name, context)
end
end
end
class StyleVariable
METADATA = %w(name kind description)
def initialize(var, text)
#var = var
#text = text
end
def method_missing(method_name)
if METADATA.include? method_name.to_s
content_of(method_name.to_s)
end
end
def value
#text.each do |string|
string =~ /^\${1}#{#var}: (.+);/
return $1 if $1
end
end
def value=(val)
#text.gsub!(/^\$#{#var}: .+;/, "$#{#var}: #{val};")
end
private
def content_of(variable)
#text.each do |string|
string =~ /^# #([\w]+[^\s]) (.+)/
return $2 if $1 == variable
end
end
end

Resources