Tracing Ruby Gmail Gem Methods Back To Their Origins - ruby

My goal is to find the place where save_attachments_to is called in this gmail gem readme example:
folder = "/where/ever"
gmail.mailbox("Faxes").emails do |email|
if !email.message.attachments.empty?
email.message.save_attachments_to(folder)
end
end
I run a "puts email.message.attachments.methods and a "email.message.attachments.class" in the loop:
Mail::AttachmentsList
guess_encoding
set_mime_type
inspect
Then I run a "puts email.message.methods and a "puts email.message.class" for good measure. The example method call is not in the list.
So I go diving into https://github.com/nu7hatch/gmail/blob/master/lib/gmail/message.rb.
No methods are defined there either, but I notice that mime/message is defined, so I go over there to look at its methods: http://rubydoc.info/gems/mime/0.1/MIME/Message
There is no save_attachments_to method here either.
Where the deuce is this method? The gmail gem does not define attachment methods, so the whole thing must be inherited from somewhere. Where? And where's the call that inherits it?

The reason you can't find it is because it doesn't exist. I'm not sure why. I downloaded the gem and played with it for a while in irb:
1.9.3-p194 :066 > x.message.attachments
=> [#<Mail::Part:70234804200840, Multipart: false, Headers: <Content-Type: application/vnd.ms-excel; name="MVBINGO.xls">, <Content-Transfer-Encoding: base64>, <Content-Disposition: attachment; filename="MVBINGO.xls">, <Content-Description: MVBINGO.xls>>]
1.9.3-p194 :063 > x.message.save_attachments_to(folder)
NoMethodError: undefined method `save_attachments_to' for #<Mail::Message:0x007fc1a3875818>
from /Users/Qsario/.rvm/gems/ruby-1.9.3-p194/gems/mail-2.4.4/lib/mail/message.rb:1289:in `method_missing'
from (irb):63
from /Users/Qsario/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
Not very helpful. Ordinarily, you can do something like
puts my_obj.method(:some_method_name).source_location
But when the method in question does not exist, that won't help you very much. EDIT: Now that I look, this exact bug is already on their issue tracker. A few people have posted code to implement the non-existent function, such as this code by a-b:
folder = Dir.pwd # for example
email.message.attachments.each do |f|
File.write(File.join(folder, f.filename), f.body.decoded)
end

Thanks for the sanity check Qsario. :-)
Here is code that works in Ruby 1.9.3 (1.9.3-p194):
gmail = Gmail.connect('username#gmail.com', 'pass')
gmail.inbox.emails.each do |email|
email.message.attachments.each do |f|
File.write(File.join(local_path, f.filename), f.body.decoded)
end
end
Here is code that works in 1.9.2 (1.9.2-p320) and 1.9.3 (1.9.3-p194):
gmail = Gmail.connect('username#gmail.com', 'pass')
gmail.inbox.emails.each do |email|
email.message.attachments.each do |file|
File.open(File.join(local_path, "name-of-file.doc or use file.filename"), "w+b", 0644 ) { |f| f.write file.body.decoded }
end
end

Related

Remote_table in spree 0.70.7 controller: #<TypeError: Zip is not a module>

I'm include gem 'remote_table' in my Gemfile (Rails 3.1.12)
In spree admin i'd create new controller:
class Admin::XlsPriceLoadsController < Admin::BaseController
def upload
source_xls = RemoteTable.new(filename)
source_xls.each do |row|
....
end
end
end
but when this action fired, i'm see the next:
TypeError (Zip is not a module):
app/controllers/admin/xls_price_loads_controller.rb:26:in `upload'
...
when i'm explore the source_xls object, a frozen? property of them is true.
So, can any soul write me, why the parsed object is frozed?
And, if i run this code
source_xls = RemoteTable.new(filename)
source_xls.each do |row|
....
end
from lib/tasks as rake task - all work fine!
Thanks for all advice!
Whereabouts does the filename method/attribute come from? Is that something from spree? What value does it have at the point #upload is called?
Regarding
app/controllers/admin/xls_price_loads_controller.rb:26:inupload'`
What's on line 26?

Ruby 1.9.3 JSON Parsing

I'm encountering a weird problem when parsing JSON with Ruby 1.9.3-p392 under RVM on CentOS 6.4. Instead of decoding embedded objects into their appropriate Ruby classes, it's just loading the object as a hash. In Ruby 1.9.3-p194 it works correctly.
Take the following sample:
require 'json'
class TestMe
attr_accessor :me
def initialize(option_hash = nil)
if option_hash
#me = option_hash['me']
end
#me ||= "Hello"
end
def to_json(*a)
{
JSON.create_id => self.class.name,
'data' => {
"me" => #me
}
}.to_json(*a)
end
def self.json_create(o)
new(o['data'])
end
end
t = TestMe.new
t.me = "foo"
t2 = JSON.parse(t.to_json)
puts t2
If I run this on Ruby 1.9.3-p194, it outputs the following:
#<TestMe:0x00000001c877f0>
If I run the same snippet on Ruby 1.9.3-p392, it outputs the following:
{"json_class"=>"TestMe", "data"=>{"me"=>"foo"}}
The behavior in p194 is what I expect and what the documentation implies. Why isn't p392 parsing the JSON data correctly?
Still not sure why/what changed, but I found a work-around. Basically, you need to construct a Parser object and pass in the :create_additions option, instead of just calling JSON.parse.
Example:
p = JSON::Parser.new(json_string, {:create_additions => true})
result = p.parse
As others have stated, it sounds like the recent change to the way JSON objects are unmarshelled. I ran into a very similar issue here and got a great answer.
JSON by itself will return a hash. There is an extension to it that gives it added capability. Try using:
require 'json'
require 'json/add/core'
I think at one point in past Rubies JSON automatically loaded the extensions but that was dropped for comparability with the JSON spec.
"add/core" includes some to_json methods to base objects and might add the capability to recover custom objects. I ran into a similar situation passing regular expressions via JSON and that was the fix.
I'm not near my computer so that isn't confirmed, but it might help.

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.

Weird error when trying to test method with argument in Mocha. Is it a bug or is it me?

It's rather hard to find any documentation on Mocha, so I'm afraid I'm totally at sea here. I have found a problem with stubbing methods that pass arguments. So for instance if I set up a class like this:
class Red
def gets(*args)
#input.gets(*args)
end
def puts(*args)
#output.puts(*args)
end
def initialize
#input = $stdin
#output = $stdout
end
private
def first_method
input = gets.chomp
if input == "test"
second_method(input)
end
end
def second_method(value)
puts value
second_method(value)
end
end
Yes it's contrived, but it's a simplification of the idea that you may have a method that you don't want called in the test.
So I might write a test such as:
setup do
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
should "pass input value to second_method" do
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
Now I would expect this to pass. But instead I get this rather arcane error message:
Errno::ENOENT: No such file or directory - getcwd
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `expand_path'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `block in filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `reject'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/backtrace_filter.rb:12:in `filtered'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/expectation_error.rb:10:in `initialize'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `new'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/mockery.rb:53:in `verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/api.rb:156:in `mocha_verify'
/Users/i0n/.rvm/gems/ruby-1.9.2-head/gems/mocha-0.9.8/lib/mocha/integration/mini_test/version_131_and_above.rb:27:in `run'
This means absolutely nothing to me, other than something deep in Mochas bowels has just gone clang. If I write the same sort of test without an argument passing to the second method I get no problem. Am I missing something?
I think it must be something in shoulda causing the problem. I use test/unit, and everything appears to be OK.
require 'rubygems'
require "test/unit"
require 'mocha'
require File.dirname(__FILE__) + '/../src/red'
class RedTest < Test::Unit::TestCase
def setup
#project = Red.new
#project.instance_variable_set(:#input, StringIO.new("test\n"))
#project.stubs(:second_method)
end
def test_description_of_thing_being_tested
#project.expects(:second_method).with("test").once
#project.instance_eval {first_method}
end
end
gives the following output:
stephen#iolanta:~/tmp/red/test # ruby red_test.rb
Loaded suite red_test
Started
.
Finished in 0.000679 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
stephen#iolanta:~/tmp/red/test #
Sorry - I've only just seen this. It's better to submit bug reports to us in Lighthouse. What documentation have you found? Have you seen the RDoc on Rubyforge? What sort of documentation were you looking for that you did not find?
I've been unable to reproduce your bug. What version of Ruby, Rubygems, Shoulda & Mocha were you using?
You can see the results of me running your test in this Gist.

Printing the source code of a Ruby block

I have a method that takes a block.
Obviously I don't know what is going to be passed in and for bizarre reasons that I won't go into here I want to print the contents of the block.
Is there a way to do this?
You can do this with Ruby2Ruby which implements a to_ruby method.
require 'rubygems'
require 'parse_tree'
require 'parse_tree_extensions'
require 'ruby2ruby'
def meth &block
puts block.to_ruby
end
meth { some code }
will output:
"proc { some(code) }"
I would also check out this awesome talk by Chris Wanstrath of Github http://goruco2008.confreaks.com/03_wanstrath.html He shows some interesting ruby2ruby and parsetree usage examples.
In Ruby 1.9+ (tested with 2.1.2), you can use https://github.com/banister/method_source
Print out the source via block#source:
#! /usr/bin/ruby
require 'rubygems'
require 'method_source'
def wait &block
puts "Running the following code: #{block.source}"
puts "Result: #{yield}"
puts "Done"
end
def run!
x = 6
wait { x == 5 }
wait { x == 6 }
end
run!
Note that in order for the source to be read you need to use a file and execute the file (testing it out from irb will result in the following error: MethodSource::SourceNotFoundError: Could not load source for : No such file or directory # rb_sysopen - (irb)
Building on Evangenieur's answer, here's Corban's answer if you had Ruby 1.9:
# Works with Ruby 1.9
require 'sourcify'
def meth &block
# Note it's to_source, not to_ruby
puts block.to_source
end
meth { some code }
My company uses this to display the Ruby code used to make carbon calculations... we used ParseTree with Ruby 1.8 and now sourcify with Ruby 1.9.
In Ruby 1.9, you can try this gem which extract the code from source file.
https://github.com/ngty/sourcify
In Ruby 2.5 the following works
puts block.source
In ruby 2.7, using the method_source gem (pry depends on it)
Set.instance_method(:merge).source.display
# =>
def merge(enum)
if enum.instance_of?(self.class)
#hash.update(enum.instance_variable_get(:#hash))
else
do_with_enum(enum) { |o| add(o) }
end
self
end
The repo says it works for procs, but I haven't tested it.

Resources