gossip 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +9 -0
- data/LICENSE.txt +34 -0
- data/Manifest.txt +62 -0
- data/README.txt +6 -0
- data/Rakefile +137 -0
- data/examples/all-config-file-choices.yml +36 -0
- data/lib/gossip.rb +32 -0
- data/lib/gossip/command.rb +122 -0
- data/lib/gossip/cronies/campfire.rb +72 -0
- data/lib/gossip/cronies/jabber.rb +66 -0
- data/lib/gossip/cronies/smtp.rb +121 -0
- data/lib/gossip/cronies/stdout.rb +24 -0
- data/lib/gossip/cronies/trac.rb +82 -0
- data/lib/gossip/cronies/twitter.rb +50 -0
- data/lib/gossip/crony.rb +102 -0
- data/lib/gossip/multi-exceptions.rb +47 -0
- data/lib/gossip/preteen.rb +86 -0
- data/lib/gossip/site-config.rb +94 -0
- data/lib/gossip/social-universe.rb +18 -0
- data/lib/gossip/version.rb +8 -0
- data/pages/classes.html +58 -0
- data/pages/cronies.html +256 -0
- data/pages/css/LICENSE.txt +1 -0
- data/pages/css/Thumbs.db +0 -0
- data/pages/css/bg2.gif +0 -0
- data/pages/css/gossip5-header-flip.jpg +0 -0
- data/pages/css/left.gif +0 -0
- data/pages/css/left_on.gif +0 -0
- data/pages/css/main.css +242 -0
- data/pages/css/right.gif +0 -0
- data/pages/css/right_on.gif +0 -0
- data/pages/css/tvline.gif +0 -0
- data/pages/images/campfire.png +0 -0
- data/pages/images/classes.png +0 -0
- data/pages/images/deployment.png +0 -0
- data/pages/images/jabber-big.png +0 -0
- data/pages/images/jabber.png +0 -0
- data/pages/images/trac-bigger.png +0 -0
- data/pages/images/trac-detail.png +0 -0
- data/pages/images/twitter.png +0 -0
- data/pages/index.html +45 -0
- data/pages/installation.html +95 -0
- data/pages/scripts.html +166 -0
- data/pages/src/classes.graffle +0 -0
- data/pages/starting-to-use.html +200 -0
- data/pages/writing-new-scripts.html +38 -0
- data/scripts/fanout +64 -0
- data/scripts/svntell +71 -0
- data/scripts/watchdog +86 -0
- data/setup.rb +1585 -0
- data/test/script/fanout-slowtests.rb +40 -0
- data/test/script/svntell-slowtests.rb +40 -0
- data/test/script/util.rb +22 -0
- data/test/script/watchdog-slowtests.rb +56 -0
- data/test/unit/command-crony-interaction-tests.rb +116 -0
- data/test/unit/command-tests.rb +119 -0
- data/test/unit/crony-tests.rb +46 -0
- data/test/unit/multi-exception-tests.rb +70 -0
- data/test/unit/preteen-tests.rb +81 -0
- data/test/util/bff.rb +45 -0
- data/test/util/doghouse.rb +42 -0
- data/test/util/silly-little-test-program.rb +6 -0
- metadata +181 -0
data/History.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Version 0.3.0
|
2
|
+
* Version from _Everyday Scripting with Ruby_ renamed from Watchdog to
|
3
|
+
to Gossip and made into a Gem.
|
4
|
+
* Generalized so that watchdog is only one example of the more general
|
5
|
+
library.
|
6
|
+
* Can now spread gossip to Campfire. (Idea taken from snitch.rubyforge.org,
|
7
|
+
John Nunemaker.)
|
8
|
+
* Can now spread gossip to Trac.
|
9
|
+
* Can now spread gossip to Twitter (credit again to John Nunemaker).
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
This software is Copyright (C) 2007 by Brian Marick <marick@exampler.com>. It is licensed according to "the Ruby license". Specifically:
|
2
|
+
|
3
|
+
You can redistribute it and/or modify it under either the terms of the GNU General Public License, version 2, <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html> or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) make other distribution arrangements with the author.
|
21
|
+
|
22
|
+
3. You may modify and include part of the software into any other
|
23
|
+
software (possibly commercial).
|
24
|
+
|
25
|
+
4. Text or files supplied as input to or produced as output from
|
26
|
+
the software do not automatically fall under the copyright of the
|
27
|
+
software, but belong to whomever generated them, and may be sold
|
28
|
+
commercially, and may be aggregated with this software.
|
29
|
+
|
30
|
+
5. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
31
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
32
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
33
|
+
PURPOSE.
|
34
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
examples/all-config-file-choices.yml
|
7
|
+
lib/gossip.rb
|
8
|
+
lib/gossip/command.rb
|
9
|
+
lib/gossip/cronies/campfire.rb
|
10
|
+
lib/gossip/cronies/jabber.rb
|
11
|
+
lib/gossip/cronies/smtp.rb
|
12
|
+
lib/gossip/cronies/stdout.rb
|
13
|
+
lib/gossip/cronies/trac.rb
|
14
|
+
lib/gossip/cronies/twitter.rb
|
15
|
+
lib/gossip/crony.rb
|
16
|
+
lib/gossip/multi-exceptions.rb
|
17
|
+
lib/gossip/preteen.rb
|
18
|
+
lib/gossip/site-config.rb
|
19
|
+
lib/gossip/social-universe.rb
|
20
|
+
lib/gossip/version.rb
|
21
|
+
pages/classes.html
|
22
|
+
pages/cronies.html
|
23
|
+
pages/css/LICENSE.txt
|
24
|
+
pages/css/Thumbs.db
|
25
|
+
pages/css/bg2.gif
|
26
|
+
pages/css/gossip5-header-flip.jpg
|
27
|
+
pages/css/left.gif
|
28
|
+
pages/css/left_on.gif
|
29
|
+
pages/css/main.css
|
30
|
+
pages/css/right.gif
|
31
|
+
pages/css/right_on.gif
|
32
|
+
pages/css/tvline.gif
|
33
|
+
pages/images/campfire.png
|
34
|
+
pages/images/classes.png
|
35
|
+
pages/images/deployment.png
|
36
|
+
pages/images/jabber-big.png
|
37
|
+
pages/images/jabber.png
|
38
|
+
pages/images/trac-bigger.png
|
39
|
+
pages/images/trac-detail.png
|
40
|
+
pages/images/twitter.png
|
41
|
+
pages/index.html
|
42
|
+
pages/installation.html
|
43
|
+
pages/scripts.html
|
44
|
+
pages/src/classes.graffle
|
45
|
+
pages/starting-to-use.html
|
46
|
+
pages/writing-new-scripts.html
|
47
|
+
scripts/fanout
|
48
|
+
scripts/svntell
|
49
|
+
scripts/watchdog
|
50
|
+
setup.rb
|
51
|
+
test/script/fanout-slowtests.rb
|
52
|
+
test/script/svntell-slowtests.rb
|
53
|
+
test/script/util.rb
|
54
|
+
test/script/watchdog-slowtests.rb
|
55
|
+
test/unit/command-crony-interaction-tests.rb
|
56
|
+
test/unit/command-tests.rb
|
57
|
+
test/unit/crony-tests.rb
|
58
|
+
test/unit/multi-exception-tests.rb
|
59
|
+
test/unit/preteen-tests.rb
|
60
|
+
test/util/bff.rb
|
61
|
+
test/util/doghouse.rb
|
62
|
+
test/util/silly-little-test-program.rb
|
data/README.txt
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Gossip is a library that provides a consistent interface to "cronies" like Twitter, Campfire, email, and Jabber. It comes with scripts that use that library to do useful things. There is a {more detailed description}[http://gossip.rubyforge.org].
|
2
|
+
|
3
|
+
There are nicely-formatted {installation instructions}[http://gossip.rubyforge.org/installation.html].
|
4
|
+
|
5
|
+
There is also a {description of the main classes}[http://gossip.rubyforge.org/classes.html] to help you understand Gossip, plus instructions for {writing new scripts}[http://gossip.rubyforge.org/writing-new-scripts.html].
|
6
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created by Brian Marick on 2007-07-03.
|
4
|
+
# Copyright (c) 2007. All rights reserved.
|
5
|
+
|
6
|
+
require 'hoe'
|
7
|
+
|
8
|
+
Dir.chdir('lib') do
|
9
|
+
# Hack: This prevents requires of the same file looking
|
10
|
+
# different in tests and causing annoying warnings.
|
11
|
+
require 'gossip/version'
|
12
|
+
end
|
13
|
+
|
14
|
+
PROJECT='gossip'
|
15
|
+
THIS_RELEASE=Gossip::Version
|
16
|
+
|
17
|
+
|
18
|
+
Hoe.new(PROJECT, THIS_RELEASE) do |p|
|
19
|
+
p.rubyforge_name = PROJECT
|
20
|
+
p.changes = "See History.txt"
|
21
|
+
p.author = "Brian Marick"
|
22
|
+
p.description = "Library to broadcast messages to multiple destinations + scripts that use it"
|
23
|
+
p.summary = p.description
|
24
|
+
p.email = "marick@exampler.com"
|
25
|
+
p.extra_deps = [['s4t-utils', '>= 1.0.3'],
|
26
|
+
['xmpp4r', '>= 0.3.1'],
|
27
|
+
['tinder', '>= 0.1.4'],
|
28
|
+
['snitch', '>= 0.1.1'],
|
29
|
+
['twitter', '>= 0.2.0'],
|
30
|
+
['user-choices', '>= 1.1.4'],
|
31
|
+
]
|
32
|
+
p.test_globs = "test/**/*tests.rb"
|
33
|
+
p.rdoc_pattern = %r{README.txt|History.txt|lib/gossip.rb|lib/gossip/.+\.rb}
|
34
|
+
p.url = "http://gossip.rubyforge.org"
|
35
|
+
p.remote_rdoc_dir = 'rdoc'
|
36
|
+
end
|
37
|
+
|
38
|
+
require 's4t-utils/rake-task-helpers'
|
39
|
+
require 's4t-utils/os'
|
40
|
+
|
41
|
+
def assert_in(dir, taskname)
|
42
|
+
unless Dir.pwd == dir
|
43
|
+
puts "Run task '#{taskname}' from directory '#{dir}'."
|
44
|
+
exit 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def confirmed_step(name, required_dir = nil)
|
49
|
+
assert_in(required_dir, name) if required_dir
|
50
|
+
STDOUT.puts "** #{name} **"
|
51
|
+
STDOUT.puts `rake #{name}`
|
52
|
+
STDOUT.print 'OK? > '
|
53
|
+
exit if STDIN.readline =~ /[nN]/
|
54
|
+
end
|
55
|
+
|
56
|
+
class HoeLike
|
57
|
+
|
58
|
+
def pull(key)
|
59
|
+
@keys[key] || raise("Missing key #{key.inspect}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(keys)
|
63
|
+
@keys = keys
|
64
|
+
project = pull(:project)
|
65
|
+
this_release = pull(:this_release)
|
66
|
+
login = pull(:login)
|
67
|
+
web_site_root = pull(:web_site_root)
|
68
|
+
export_root = pull(:export_root)
|
69
|
+
|
70
|
+
root = "svn+ssh://#{login}/var/svn/#{project}"
|
71
|
+
project_exports = "#{export_root}/#{project}"
|
72
|
+
|
73
|
+
desc "Run fast tests."
|
74
|
+
task 'fast' do
|
75
|
+
S4tUtils.run_particular_tests('test', 'fast')
|
76
|
+
end
|
77
|
+
|
78
|
+
desc "Run slow tests."
|
79
|
+
task 'slow' do
|
80
|
+
S4tUtils.run_particular_tests('test', 'slow')
|
81
|
+
end
|
82
|
+
|
83
|
+
desc "Upload all the web pages"
|
84
|
+
task 'upload_pages' do | task |
|
85
|
+
exec = "scp -r #{web_site_root}/* #{login}:/var/www/gforge-projects/#{project}/"
|
86
|
+
puts exec
|
87
|
+
system(exec)
|
88
|
+
end
|
89
|
+
|
90
|
+
desc "Tag release with current version."
|
91
|
+
task 'tag_release' do
|
92
|
+
from = "#{root}/trunk"
|
93
|
+
to = "#{root}/tags/rel-#{this_release}"
|
94
|
+
message = "Release #{this_release}"
|
95
|
+
exec = "svn copy -m '#{message}' #{from} #{to}"
|
96
|
+
puts exec
|
97
|
+
system(exec)
|
98
|
+
end
|
99
|
+
|
100
|
+
desc "Export to #{project_exports}"
|
101
|
+
task 'export' do
|
102
|
+
Dir.chdir(export_root) do
|
103
|
+
rm_rf project
|
104
|
+
exec = "svn export #{root}/trunk #{project}"
|
105
|
+
puts exec
|
106
|
+
system exec
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
desc "Complete release of everything - asks for confirmation after steps"
|
112
|
+
# Because in Ruby 1.8.6, Rake doesn't notice subtask failures, so it
|
113
|
+
# won't stop for us.
|
114
|
+
task 'release_everything' do
|
115
|
+
confirmed_step 'check_manifest'
|
116
|
+
confirmed_step 'test'
|
117
|
+
confirmed_step 'export'
|
118
|
+
Dir.chdir(project_exports) do
|
119
|
+
puts "Working in #{Dir.pwd}"
|
120
|
+
confirmed_step 'upload_pages', project_exports
|
121
|
+
confirmed_step 'publish_docs', project_exports
|
122
|
+
ENV['VERSION'] = this_release
|
123
|
+
confirmed_step 'release', project_exports
|
124
|
+
end
|
125
|
+
confirmed_step 'tag_release'
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
HoeLike.new(:project => PROJECT, :this_release => THIS_RELEASE,
|
133
|
+
:login => "marick@rubyforge.org",
|
134
|
+
:web_site_root => 'pages',
|
135
|
+
:export_root => "#{S4tUtils.find_home}/tmp/exports")
|
136
|
+
|
137
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Turn cronies (destinations) on or off.
|
2
|
+
|
3
|
+
standard-output: true
|
4
|
+
jabber: false
|
5
|
+
mail: false
|
6
|
+
campfire: false
|
7
|
+
trac: false
|
8
|
+
twitter: false
|
9
|
+
|
10
|
+
# Configurations for each destination.
|
11
|
+
# You can delete or ignore ones you don't plan to use.
|
12
|
+
|
13
|
+
jabber-account: jabber@example.com
|
14
|
+
jabber-password: not-the-real-one
|
15
|
+
jabber-to:
|
16
|
+
- brianmarick@example.com
|
17
|
+
|
18
|
+
mail-to:
|
19
|
+
- marick@example.com
|
20
|
+
- dawn@example.com
|
21
|
+
|
22
|
+
mail-from: bmarick@mac.com
|
23
|
+
mail-server: smtp.mac.com
|
24
|
+
mail-port: 587
|
25
|
+
mail-from-domain: localhost.localdomain
|
26
|
+
mail-account: bmarick
|
27
|
+
mail-password: not-the-real-one
|
28
|
+
mail-authentication: login
|
29
|
+
|
30
|
+
campfire-login: marick@example.com
|
31
|
+
campfire-password: not-the-real-one
|
32
|
+
campfire-subdomain: example
|
33
|
+
campfire-room: room
|
34
|
+
|
35
|
+
twitter-login: twittername
|
36
|
+
twitter-password: some password
|
data/lib/gossip.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#! /opt/local/bin/ruby
|
2
|
+
#
|
3
|
+
# Created by Brian Marick on 2007-09-15.
|
4
|
+
# Copyright (c) 2007. All rights reserved.
|
5
|
+
|
6
|
+
require 's4t-utils'
|
7
|
+
require 'active_support' # because it includes its own copy of a gem
|
8
|
+
# that user-choices would otherwise include.
|
9
|
+
|
10
|
+
require 'gossip/social-universe'
|
11
|
+
require 'gossip/preteen'
|
12
|
+
require 'gossip/crony'
|
13
|
+
require 'gossip/command'
|
14
|
+
require 'gossip/multi-exceptions'
|
15
|
+
require 'gossip/version'
|
16
|
+
|
17
|
+
# Gossip is a library used to write scripts that collect useful information
|
18
|
+
# and send it to various places (like jabber, email, trac, etc.). The
|
19
|
+
# guiding metaphor is of a Preteen who has Cronies. A Crony is someone who
|
20
|
+
# might conceivably hear gossip from the Preteen. However, some cronies are
|
21
|
+
# Best Friends Forever, and others are (temporarily) not speaking to the
|
22
|
+
# Preteen. Only the former hear the gossip.
|
23
|
+
#
|
24
|
+
# There's also a whole bunch of people who are not Cronies. We won't speak
|
25
|
+
# of them. All we need know of them is that they can never ever receive
|
26
|
+
# gossip because they can never ever become a Best Friend Forever.
|
27
|
+
#
|
28
|
+
# Most of the code in the script will be in a subclass of GossipCommand.
|
29
|
+
# The execute method of that command gathers the information and passes
|
30
|
+
# it to the Preteen.
|
31
|
+
module Gossip
|
32
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#! /opt/local/bin/ruby
|
2
|
+
#
|
3
|
+
# Created by Brian Marick on 2007-09-15.
|
4
|
+
# Copyright (c) 2007. All rights reserved.
|
5
|
+
|
6
|
+
require 'user-choices'
|
7
|
+
require 'pp'
|
8
|
+
require 'gossip/preteen'
|
9
|
+
|
10
|
+
module Gossip
|
11
|
+
|
12
|
+
|
13
|
+
# THe GossipCommand knows how to set up a Preteen's Crony's. A particular
|
14
|
+
# script should subclass it to add script-specific behavior. See the
|
15
|
+
# examples, particularly fanout.rb, for details. To understand more about
|
16
|
+
# GossipCommand, see UserChoices::Command in the user-choices gem.
|
17
|
+
# http://user-choices.rubyforge.org.
|
18
|
+
class GossipCommand < UserChoices::Command
|
19
|
+
include UserChoices
|
20
|
+
|
21
|
+
attr_accessor :preteen
|
22
|
+
def initialize(preteen)
|
23
|
+
self.preteen = preteen
|
24
|
+
super()
|
25
|
+
end
|
26
|
+
|
27
|
+
# Text describing the command-line arguments (not the options).
|
28
|
+
# Traditionally looks something like:
|
29
|
+
# "Usage: ruby #{$0} [options] args..."
|
30
|
+
# Can be a single line or array of lines.
|
31
|
+
# Must be overridden.
|
32
|
+
def usage; subclass_responsibility; end
|
33
|
+
|
34
|
+
# Override to name the configuration file specific to this script.
|
35
|
+
def script_config_file; subclass_responsibility; end
|
36
|
+
|
37
|
+
def add_choices(builder)
|
38
|
+
first_set_are_specific_to_script(builder)
|
39
|
+
all_crony_switches_come_next(builder)
|
40
|
+
crony_specific_switches_are_third(builder)
|
41
|
+
helpful_switches_go_last(builder)
|
42
|
+
|
43
|
+
# Can go anywhere - doesn't appear in help text.
|
44
|
+
add_arglist_choice(builder)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# By default, the command will stuff all arguments into
|
49
|
+
# a choice named :arglist.
|
50
|
+
def add_arglist_choice(builder)
|
51
|
+
builder.add_choice(:arglist) { | command_line |
|
52
|
+
command_line.uses_arglist
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def postprocess_user_choices
|
57
|
+
if @user_choices[:choices]
|
58
|
+
puts "Looking for configuration information in:"
|
59
|
+
puts " " + File.join(S4tUtils.find_home, script_config_file)
|
60
|
+
puts " " + File.join(S4tUtils.find_home, gossip_config_file)
|
61
|
+
puts "Choices gathered from all sources:"
|
62
|
+
puts alphabetical_symbol_hash(@user_choices)
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
|
66
|
+
preteen.cronies.each do | crony |
|
67
|
+
crony.user_choices = @user_choices
|
68
|
+
crony.postprocess_user_choices
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def describe_all_but_options
|
73
|
+
[usage,
|
74
|
+
"Site-wide defaults are noted below.",
|
75
|
+
"Override them in the '#{script_config_file}' or '#{gossip_config_file}' files in your home folder."
|
76
|
+
].flatten
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def first_set_are_specific_to_script(builder)
|
82
|
+
if self.respond_to?(:add_choices_specific_to_script)
|
83
|
+
builder.section_specific_to_script do
|
84
|
+
add_choices_specific_to_script(builder)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def all_crony_switches_come_next(builder)
|
90
|
+
builder.section("that determine who hears the gossip") do
|
91
|
+
preteen.cronies.each do | crony |
|
92
|
+
crony.add_bff_choice(builder)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def crony_specific_switches_are_third(builder)
|
98
|
+
builder.section("that apply to particular listeners") do
|
99
|
+
preteen.cronies.each do | crony |
|
100
|
+
crony.add_configuration_choices(builder)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def helpful_switches_go_last(builder)
|
106
|
+
builder.add_choice(:choices,
|
107
|
+
:default => false,
|
108
|
+
:type => :boolean) { | command_line |
|
109
|
+
command_line.uses_switch("--choices",
|
110
|
+
"Show all configuration choices.")
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def alphabetical_symbol_hash(hash)
|
115
|
+
key_strings = hash.keys.collect { |k| k.to_s }.sort
|
116
|
+
keys = key_strings.collect { |s| s.to_sym }
|
117
|
+
keys.collect do |key|
|
118
|
+
" #{key.inspect}=>#{hash[key].inspect}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|