How to prevent Ruby plist converting ' to &#39? - ruby

I'm using plist gem to manipulate existing plist, however, after saving I found the single quote (represented as ' being converted to &#39), the script is used to modify a plist used by an XCode project, which is also editable in XCode, and XCode saves single quote as '.
So my question is: is there a way to force Ruby to use the same as XCode? I guess if not, the last resort is to add some line which manually converts all ' to ' after running the Ruby script.
Thanks!

The plist gem uses CGI.escapeHTML which escapes special characters '&"<>:
require 'cgi'
CGI.escapeHTML("foo'bar")
#=> "foo'bar"
If we take a look at its implementation, we see that it's actually Ruby code. The method is basically a gsub call using a hash: (I'm omitting some encoding stuff)
module CGI::Util
# ...
# The set of special characters and their escaped values
TABLE_FOR_ESCAPE_HTML__ = {
"'" => ''',
'&' => '&',
'"' => '"',
'<' => '<',
'>' => '>',
}
# Escape special characters in HTML, namely '&\"<>
# CGI.escapeHTML('Usage: foo "bar" <baz>')
# # => "Usage: foo "bar" <baz>"
def escapeHTML(string)
# ...
string.gsub(/['&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
end
# ...
end
So we can just change the constant to fit our needs, right?
CGI::TABLE_FOR_ESCAPE_HTML__["'"] = "&apos;"
CGI.escapeHTML("foo'bar")
#=> "foo'bar" <- ???
Why does escapeHTML still return '? It turns out that right after the method definition, a library called cgi/escape is included:
begin
require 'cgi/escape'
rescue LoadError
end
And guess what - cgi/escape overrides escapeHTML by a C implementation with hard-coded values for performance reasons.
However, not all is lost. We could try to revert the C implementation to its original form:
method = CGI::Util.instance_method(:escapeHTML)
unless method.owner == CGI::Util
method = method.super_method until method.owner == CGI::Util
CGI::define_singleton_method(:escapeHTML, method)
end
CGI::TABLE_FOR_ESCAPE_HTML__["'"] = '&apos;'
CGI.escapeHTML("foo'bar")
#=> "foo&apos;bar"
But that would change CGI.escapeHTML globally which might have undesired side effects.
A less intrusive way is to implement a CGI module under the Plist namespace with a custom escapeHTML method: (so only plist will use this implementation)
require 'plist'
module Plist
class CGI
TABLE_FOR_ESCAPE_HTML__ = ::CGI::TABLE_FOR_ESCAPE_HTML__.merge("'" => '&apos;')
def self.escapeHTML(string)
string.gsub(/['&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
end
end
end
puts Plist::Emit.dump("foo'bar")
Output:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>foo&apos;bar</string>
</plist>
The gem should probably avoid CGI.escapeHTML and provide its own escape mechanism to be Apple/XCode compliant. (you might want to open a feature request)

Related

How to inspect the body of a method? [duplicate]

I would like to know whether I can get source code a method on the fly, and whether I can get which file is this method in.
like
A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE
Use source_location:
class A
def foo
end
end
file, line = A.instance_method(:foo).source_location
# or
file, line = A.new.method(:foo).source_location
puts "Method foo is defined in #{file}, line #{line}"
# => "Method foo is defined in temp.rb, line 2"
Note that for builtin methods, source_location returns nil. If want to check out the C source code (have fun!), you'll have to look for the right C file (they're more or less organized by class) and find the rb_define_method for the method (towards the end of the file).
In Ruby 1.8 this method does not exist, but you can use this gem.
None of the answers so far show how to display the source code of a method on the fly...
It's actually very easy if you use the awesome 'method_source' gem by John Mair (the maker of Pry):
The method has to be implemented in Ruby (not C), and has to be loaded from a file (not irb).
Here's an example displaying the method source code in the Rails console with method_source:
$ rails console
> require 'method_source'
> I18n::Backend::Simple.instance_method(:lookup).source.display
def lookup(locale, key, scope = [], options = {})
init_translations unless initialized?
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
keys.inject(translations) do |result, _key|
_key = _key.to_sym
return nil unless result.is_a?(Hash) && result.has_key?(_key)
result = result[_key]
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
result
end
end
=> nil
See also:
https://rubygems.org/gems/method_source
https://github.com/banister/method_source
http://banisterfiend.wordpress.com/
Here is how to print out the source code from ruby:
puts File.read(OBJECT_TO_GET.method(:METHOD_FROM).source_location[0])
Without dependencies
method = SomeConstant.method(:some_method_name)
file_path, line = method.source_location
# puts 10 lines start from the method define
IO.readlines(file_path)[line-1, 10]
If you want use this more conveniently, your can open the Method class:
# ~/.irbrc
class Method
def source(limit=10)
file, line = source_location
if file && line
IO.readlines(file)[line-1,limit]
else
nil
end
end
end
And then just call method.source
With Pry you can use the show-method to view a method source, and you can even see some ruby c source code with pry-doc installed, according pry's doc in codde-browing
Note that we can also view C methods (from Ruby Core) using the
pry-doc plugin; we also show off the alternate syntax for show-method:
pry(main)> show-method Array#select
From: array.c in Ruby Core (C Method):
Number of lines: 15
static VALUE
rb_ary_select(VALUE ary)
{
VALUE result;
long i;
RETURN_ENUMERATOR(ary, 0, 0);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
rb_ary_push(result, rb_ary_elt(ary, i));
}
}
return result;
}
I created the "ri_for" gem for this purpose
>> require 'ri_for'
>> A.ri_for :foo
... outputs the source (and location, if you're on 1.9).
GL.
-r
Internal methods don't have source or source location (e.g. Integer#to_s)
require 'method_source'
User.method(:last).source
User.method(:last).source_location
I had to implement a similar feature (grab the source of a block) as part of Wrong and you can see how (and maybe even reuse the code) in chunk.rb (which relies on Ryan Davis' RubyParser as well as some pretty funny source file glomming code). You'd have to modify it to use Method#source_location and maybe tweak some other things so it does or doesn't include the def.
BTW I think Rubinius has this feature built in. For some reason it's been left out of MRI (the standard Ruby implementation), hence this hack.
Oooh, I like some of the stuff in method_source! Like using eval to tell if an expression is valid (and keep glomming source lines until you stop getting parse errors, like Chunk does)...

Is there a way to specify ruby class/module constant as value in Ruby YAML file

I have a YAML config file where I want to include specific ruby class/module constants instead of the actual value.
For example, instead of putting "loglevel: 0" in the config file, I want "loglevel: Logger::DEBUG".
Is there a way to have YAML decode or resolve a class or module constant like Logger::DEBUG?
This is what I've been playing with, but looking at the psych ruby code, I don't see anything that might support this.
config.yml
loglevel: !ruby/class:fixnum Logger::DEBUG
In irb
irb> require 'logger'
irb> config = YAML.load_file('config.yml')
config['loglevel'] contains "Logger::DEBUG" as a String instead of the actual value.
I can do an eval on it like so:
irb> p eval config['loglevel']
0
==> 0
I'm just wondering if there's a way to have YAML eval it? I'm okay with doing it in my code after doing a YAML load, but I wanted to make sure I left no stone unturned in my, what has turned into a lengthy, quest ;-).
Not sure of a YAML way, but best not to use eval...
In Ruby 2+
Object.const_get 'Logger::DEBUG'
Or the old school
def const_lookup const_name
const_name.split('::').inject(Object) do |rec, name|
rec.const_get(name)
end
end
const_lookup 'Logger::DEBUG'
Looks like you can do it for classes/modules but not their constants
2.0.0-p247 :046 > YAML.load("!ruby/class 'String'")
=> String
2.0.0-p247 :047 > YAML.load("!ruby/class 'String'").class
=> Class
2.0.0-p247 :065 > YAML.load("!ruby/class 'Logger'")
=> Logger

converting from xml name-values into simple hash

I don't know what name this goes by and that's been complicating my search.
My data file OX.session.xml is in the (old?) form
<?xml version="1.0" encoding="utf-8"?>
<CAppLogin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://oxbranch.optionsxpress.com">
<SessionID>FE5E27A056944FBFBEF047F2B99E0BF6</SessionID>
<AccountNum>8228-5500</AccountNum>
<AccountID>967454</AccountID>
</CAppLogin>
What is that XML data format called exactly?
Anyway, all I want is to end up with one hash in my Ruby code like so:
CAppLogin = { :SessionID => "FE5E27A056944FBFBEF047F2B99E0BF6", :AccountNum => "8228-5500", etc. } # Doesn't have to be called CAppLogin as in the file, may be fixed
What might be shortest, most built-in Ruby way to automate that hash read, in a way I can update the SessionID value and store it easily back into the file for later program runs?
I've played around with YAML, REXML but would rather not yet print my (bad) example trials.
There are a few libraries you can use in Ruby to do this.
Ruby toolbox has some good coverage of a few of them:
https://www.ruby-toolbox.com/categories/xml_mapping
I use XMLSimple, just require the gem then load in your xml file using xml_in:
require 'xmlsimple'
hash = XmlSimple.xml_in('session.xml')
If you're in a Rails environment, you can just use Active Support:
require 'active_support'
session = Hash.from_xml('session.xml')
Using Nokogiri to parse the XML with namespaces:
require 'nokogiri'
dom = Nokogiri::XML(File.read('OX.session.xml'))
node = dom.xpath('ox:CAppLogin',
'ox' => "http://oxbranch.optionsxpress.com").first
hash = node.element_children.each_with_object(Hash.new) do |e, h|
h[e.name.to_sym] = e.content
end
puts hash.inspect
# {:SessionID=>"FE5E27A056944FBFBEF047F2B99E0BF6",
# :AccountNum=>"8228-5500", :AccountID=>"967454"}
If you know that the CAppLogin is the root element, you can simplify a bit:
require 'nokogiri'
dom = Nokogiri::XML(File.read('OX.session.xml'))
hash = dom.root.element_children.each_with_object(Hash.new) do |e, h|
h[e.name.to_sym] = e.content
end
puts hash.inspect
# {:SessionID=>"FE5E27A056944FBFBEF047F2B99E0BF6",
# :AccountNum=>"8228-5500", :AccountID=>"967454"}

Is there a way to print Ruby function source, like JavaScript's Function#toString / toSource? [duplicate]

I would like to know whether I can get source code a method on the fly, and whether I can get which file is this method in.
like
A.new.method(:a).SOURCE_CODE
A.new.method(:a).FILE
Use source_location:
class A
def foo
end
end
file, line = A.instance_method(:foo).source_location
# or
file, line = A.new.method(:foo).source_location
puts "Method foo is defined in #{file}, line #{line}"
# => "Method foo is defined in temp.rb, line 2"
Note that for builtin methods, source_location returns nil. If want to check out the C source code (have fun!), you'll have to look for the right C file (they're more or less organized by class) and find the rb_define_method for the method (towards the end of the file).
In Ruby 1.8 this method does not exist, but you can use this gem.
None of the answers so far show how to display the source code of a method on the fly...
It's actually very easy if you use the awesome 'method_source' gem by John Mair (the maker of Pry):
The method has to be implemented in Ruby (not C), and has to be loaded from a file (not irb).
Here's an example displaying the method source code in the Rails console with method_source:
$ rails console
> require 'method_source'
> I18n::Backend::Simple.instance_method(:lookup).source.display
def lookup(locale, key, scope = [], options = {})
init_translations unless initialized?
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
keys.inject(translations) do |result, _key|
_key = _key.to_sym
return nil unless result.is_a?(Hash) && result.has_key?(_key)
result = result[_key]
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
result
end
end
=> nil
See also:
https://rubygems.org/gems/method_source
https://github.com/banister/method_source
http://banisterfiend.wordpress.com/
Here is how to print out the source code from ruby:
puts File.read(OBJECT_TO_GET.method(:METHOD_FROM).source_location[0])
Without dependencies
method = SomeConstant.method(:some_method_name)
file_path, line = method.source_location
# puts 10 lines start from the method define
IO.readlines(file_path)[line-1, 10]
If you want use this more conveniently, your can open the Method class:
# ~/.irbrc
class Method
def source(limit=10)
file, line = source_location
if file && line
IO.readlines(file)[line-1,limit]
else
nil
end
end
end
And then just call method.source
With Pry you can use the show-method to view a method source, and you can even see some ruby c source code with pry-doc installed, according pry's doc in codde-browing
Note that we can also view C methods (from Ruby Core) using the
pry-doc plugin; we also show off the alternate syntax for show-method:
pry(main)> show-method Array#select
From: array.c in Ruby Core (C Method):
Number of lines: 15
static VALUE
rb_ary_select(VALUE ary)
{
VALUE result;
long i;
RETURN_ENUMERATOR(ary, 0, 0);
result = rb_ary_new2(RARRAY_LEN(ary));
for (i = 0; i < RARRAY_LEN(ary); i++) {
if (RTEST(rb_yield(RARRAY_PTR(ary)[i]))) {
rb_ary_push(result, rb_ary_elt(ary, i));
}
}
return result;
}
I created the "ri_for" gem for this purpose
>> require 'ri_for'
>> A.ri_for :foo
... outputs the source (and location, if you're on 1.9).
GL.
-r
Internal methods don't have source or source location (e.g. Integer#to_s)
require 'method_source'
User.method(:last).source
User.method(:last).source_location
I had to implement a similar feature (grab the source of a block) as part of Wrong and you can see how (and maybe even reuse the code) in chunk.rb (which relies on Ryan Davis' RubyParser as well as some pretty funny source file glomming code). You'd have to modify it to use Method#source_location and maybe tweak some other things so it does or doesn't include the def.
BTW I think Rubinius has this feature built in. For some reason it's been left out of MRI (the standard Ruby implementation), hence this hack.
Oooh, I like some of the stuff in method_source! Like using eval to tell if an expression is valid (and keep glomming source lines until you stop getting parse errors, like Chunk does)...

Ruby unable to use require

This is a newbie question as I am attempting to learn Ruby by myself, so apologies if it sounds like a silly question!
I am reading through the examples of why's (poignant) guide to ruby and am in chapter 4. I typed the code_words Hash into a file called wordlist.rb
I opened another file and typed the first line as require 'wordlist.rb' and the rest of the code as below
#Get evil idea and swap in code
print "Enter your ideas "
idea = gets
code_words.each do |real, code|
idea.gsub!(real, code)
end
#Save the gibberish to a new file
print "File encoded, please enter a name to save the file"
ideas_name = gets.strip
File::open( 'idea-' + ideas_name + '.txt', 'w' ) do |f|
f << idea
end
When I execute this code, it fails with the following error message:
C:/MyCode/MyRubyCode/filecoder.rb:5: undefined local variable or method `code_words' for main:Object (NameError)
I use Windows XP and Ruby version ruby 1.8.6
I know I should be setting something like a ClassPath, but not sure where/how to do so!
Many thanks in advance!
While the top-level of all files are executed in the same context, each file has its own script context for local variables. In other words, each file has its own set of local variables that can be accessed throughout that file, but not in other files.
On the other hand, constants (CodeWords), globals ($code_words) and methods (def code_words) would be accessible across files.
Some solutions:
CodeWords = {:real => "code"}
$code_words = {:real => "code"}
def code_words
{:real => "code"}
end
An OO solution that is definitely too complex for this case:
# first file
class CodeWords
DEFAULT = {:real => "code"}
attr_reader :words
def initialize(words = nil)
#words = words || DEFAULT
end
end
# second file
print "Enter your ideas "
idea = gets
code_words = CodeWords.new
code_words.words.each do |real, code|
idea.gsub!(real, code)
end
#Save the gibberish to a new file
print "File encoded, please enter a name to save the file"
ideas_name = gets.strip
File::open( 'idea-' + ideas_name + '.txt', 'w' ) do |f|
f << idea
end
I think the problem might be that the require executes the code in another context, so the runtime variable is no longer available after the require.
What you could try is making it a constant:
CodeWords = { :real => 'code' }
That will be available everywhere.
Here is some background on variable scopes etc.
I was just looking at the same example and was having the same problem.
What I did was change the variable name in both files from code_words to $code_words .
This would make it a global variable and thus accesible by both files right?
My question is: wouldn't this be a simpler solution than making it a constant and having to write CodeWords = { :real => 'code' } or is there a reason not to do it ?
A simpler way would be to use the Marshal.dump feature to save the code words.
# Save to File
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'
}
# Serialize
f = File.open('codewords','w')
Marshal.dump(code_words, f)
f.close
Now at the beginning of your file you would put this:
# Load the Serialized Data
code_words = Marshal.load(File.open('codewords','r'))
Here's the easy way to make sure you can always include a file that's in the same directory as your app, put this before the require statement
$:.unshift File.dirname(__FILE__)
$: is the global variable representing the "CLASSPATH"

Resources