stella 0.8.8.001 → 2.0.1.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGES.txt +9 -1
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +50 -0
  4. data/README.md +5 -79
  5. data/Rakefile +10 -7
  6. data/Rudyfile +1 -1
  7. data/TODO +31 -0
  8. data/VERSION.yml +3 -4
  9. data/bin/stella +23 -81
  10. data/certs/README.txt +17 -0
  11. data/certs/cacerts.pem +1529 -0
  12. data/certs/gd-class2-root.crt +24 -0
  13. data/certs/gd_bundle.crt +76 -0
  14. data/certs/gd_intermediate.crt +29 -0
  15. data/certs/startssl-ca.pem +44 -0
  16. data/certs/startssl-sub.class1.server.ca.pem +36 -0
  17. data/certs/stella-master.crt +1738 -0
  18. data/lib/stella.rb +191 -123
  19. data/lib/stella/cli.rb +47 -67
  20. data/lib/stella/client.rb +424 -360
  21. data/lib/stella/core_ext.rb +527 -0
  22. data/lib/stella/engine.rb +126 -419
  23. data/lib/stella/report.rb +391 -0
  24. data/lib/stella/testplan.rb +432 -306
  25. data/lib/stella/utils.rb +227 -2
  26. data/stella.gemspec +56 -55
  27. data/try/00_basics_try.rb +29 -0
  28. data/try/01_selectable_try.rb +25 -0
  29. data/try/09_utils_try.rb +67 -0
  30. data/try/10_stella_object_try.rb +49 -0
  31. data/try/40_report_try.rb +133 -0
  32. data/try/90_class_syntax_try.rb +13 -0
  33. data/try/emhttp.rb +62 -0
  34. data/try/rubyroute.rb +70 -0
  35. data/try/support/file.bmp +0 -0
  36. data/try/support/file.gif +0 -0
  37. data/try/support/file.ico +0 -0
  38. data/try/support/file.jpeg +0 -0
  39. data/try/support/file.jpg +0 -0
  40. data/try/support/file.png +0 -0
  41. data/try/traceviz.rb +60 -0
  42. data/vendor/httpclient-2.1.5.2/httpclient/session.rb +5 -2
  43. metadata +81 -53
  44. data/examples/cookies/plan.rb +0 -49
  45. data/examples/csvdata/plan.rb +0 -32
  46. data/examples/csvdata/search_terms.csv +0 -14
  47. data/examples/dynamic/plan.rb +0 -60
  48. data/examples/essentials/logo.png +0 -0
  49. data/examples/essentials/plan.rb +0 -248
  50. data/examples/essentials/search_terms.txt +0 -19
  51. data/examples/exceptions/plan.rb +0 -20
  52. data/examples/httpauth/plan.rb +0 -33
  53. data/examples/timeout/plan.rb +0 -18
  54. data/examples/variables/plan.rb +0 -41
  55. data/lib/stella/client/container.rb +0 -378
  56. data/lib/stella/common.rb +0 -363
  57. data/lib/stella/data.rb +0 -59
  58. data/lib/stella/data/http.rb +0 -189
  59. data/lib/stella/engine/functional.rb +0 -156
  60. data/lib/stella/engine/load.rb +0 -516
  61. data/lib/stella/guidelines.rb +0 -18
  62. data/lib/stella/logger.rb +0 -150
  63. data/lib/stella/utils/httputil.rb +0 -266
  64. data/try/01_numeric_mixins_tryouts.rb +0 -40
  65. data/try/12_digest_tryouts.rb +0 -42
  66. data/try/70_module_usage.rb +0 -21
  67. data/try/api/10_functional.rb +0 -20
  68. data/try/configs/failed_requests.rb +0 -31
  69. data/try/configs/global_sequential.rb +0 -18
  70. data/try/proofs/thread_queue.rb +0 -21
data/lib/stella/utils.rb CHANGED
@@ -1,13 +1,235 @@
1
-
2
1
  require 'socket' # Why doesn't socket work with autoload?
3
2
  autoload :Timeout, 'timeout'
3
+ autoload :IPAddr, 'ipaddr'
4
+ autoload :Whois, 'whois'
5
+ autoload :PublicSuffixService, 'public_suffix_service'
4
6
 
5
- module Stella
7
+ class Stella
8
+
9
+ IMAGE_EXT = %w/.bmp .gif .jpg .jpeg .png .ico/ unless defined?(Stella::IMAGE_EXT)
6
10
 
7
11
  # A motley collection of methods that Stella loves to call!
8
12
  module Utils
9
13
  extend self
10
14
  include Socket::Constants
15
+
16
+ ADDR_LOCAL = IPAddr.new("127.0.0.0/8")
17
+ ADDR_CLASSA = IPAddr.new("10.0.0.0/8")
18
+ ADDR_CLASSB = IPAddr.new("172.16.0.0/16")
19
+ ADDR_CLASSC = IPAddr.new("192.168.0.0/24")
20
+
21
+ # See: https://forums.aws.amazon.com/ann.jspa?annID=877
22
+ ADDR_EC2_US_EAST = %w{
23
+ 216.182.224.0/20
24
+ 72.44.32.0/19
25
+ 67.202.0.0/18
26
+ 75.101.128.0/17
27
+ 174.129.0.0/16
28
+ 204.236.192.0/18
29
+ 184.73.0.0/16
30
+ 184.72.128.0/17
31
+ 184.72.64.0/18
32
+ 50.16.0.0/15
33
+ }.collect { |ipr| IPAddr.new(ipr.strip) }
34
+
35
+ ADDR_EC2_US_WEST = %w{
36
+ 204.236.128.0/18
37
+ 184.72.0.0/18
38
+ 50.18.0.0/18
39
+ }.collect { |ipr| IPAddr.new(ipr.strip) }
40
+
41
+ ADDR_EC2_EU_WEST = %w{
42
+ 79.125.0.0/17
43
+ 46.51.128.0/18
44
+ 46.51.192.0/20
45
+ 46.137.0.0/17
46
+ }.collect { |ipr| IPAddr.new(ipr.strip) }
47
+
48
+ ADDR_EC2_AP_EAST = %w{
49
+ 175.41.128.0/18
50
+ 122.248.192.0/18
51
+ }.collect { |ipr| IPAddr.new(ipr.strip) }
52
+
53
+
54
+ def image_ext?(name)
55
+ IMAGE_EXT.include?(File.extname(name.downcase))
56
+ end
57
+
58
+ def image?(s)
59
+ return false if s.nil?
60
+ (bmp?(s) || jpg?(s) || png?(s) || gif?(s) || ico?(s))
61
+ end
62
+
63
+ # Checks if the file has more than 30% non-ASCII characters.
64
+ # NOTE: how to determine the difference between non-latin and binary?
65
+ def binary?(s)
66
+ return false if s.nil?
67
+ #puts "TODO: fix encoding issue in 1.9"
68
+ s = s.to_s.split(//) rescue [] unless Array === s
69
+ s.slice!(0, 4096) # limit to a typcial blksize
70
+ ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
71
+ end
72
+
73
+ # Based on ptools by Daniel J. Berger
74
+ # http://raa.ruby-lang.org/project/ptools/
75
+ def bmp?(a)
76
+ possible = ['BM6', 'BM' << 226.chr]
77
+ possible.member? a.slice(0, 3)
78
+ end
79
+
80
+ # Based on ptools by Daniel J. Berger
81
+ # http://raa.ruby-lang.org/project/ptools/
82
+ def jpg?(a)
83
+ a.slice(0, 10) == "\377\330\377\340\000\020JFIF"
84
+ end
85
+
86
+ # Based on ptools by Daniel J. Berger
87
+ # http://raa.ruby-lang.org/project/ptools/
88
+ def png?(a)
89
+ a.slice(0, 4) == "\211PNG"
90
+ end
91
+
92
+ def ico?(a)
93
+ a.slice(0, 3) == [0.chr, 0.chr, 1.chr].join
94
+ end
95
+
96
+ # Based on ptools by Daniel J. Berger
97
+ # http://raa.ruby-lang.org/project/ptools/
98
+ def gif?(a)
99
+ ['GIF89a', 'GIF97a'].include?(a.slice(0, 6))
100
+ end
101
+
102
+ def domain(host)
103
+ begin
104
+ PublicSuffixService.parse host
105
+ rescue PublicSuffixService::DomainInvalid => ex
106
+ Stella.ld ex.message
107
+ nil
108
+ rescue => ex
109
+ Stella.li "Error determining domain for #{host}: #{ex.message} (#{ex.class})"
110
+ Stella.ld ex.backtrace
111
+ nil
112
+ end
113
+ end
114
+
115
+ def whois(host_or_ip)
116
+ begin
117
+ raw = Whois.whois(host_or_ip)
118
+ info = raw.content.split("\n").select { |line| line !~ /\A[\#\%]/ && !line.empty? }
119
+ info.join("\n")
120
+ rescue => ex
121
+ Stella.ld "Error fetching whois for #{host_or_ip}: #{ex.message}"
122
+ Stella.ld ex.backtrace
123
+ end
124
+ end
125
+
126
+ # Returns an Array of ip addresses or nil
127
+ def ipaddr(host)
128
+ require 'resolv'
129
+ host = host.host if host.kind_of?(URI)
130
+ begin
131
+ resolv = Resolv::DNS.new # { :nameserver => [] }
132
+ resolv.getaddresses(host).collect { |addr| addr.to_s }
133
+ rescue => ex
134
+ Stella.ld "Error getting ip address for #{host}: #{ex.message} (#{ex.class})"
135
+ Stella.ld ex.backtrace
136
+ nil
137
+ end
138
+ end
139
+
140
+ # http://www.opensource.apple.com/source/ruby/ruby-4/ruby/lib/resolv.rb
141
+ # * Resolv::DNS::Resource::IN::ANY
142
+ # * Resolv::DNS::Resource::IN::NS
143
+ # * Resolv::DNS::Resource::IN::CNAME
144
+ # * Resolv::DNS::Resource::IN::SOA
145
+ # * Resolv::DNS::Resource::IN::HINFO
146
+ # * Resolv::DNS::Resource::IN::MINFO
147
+ # * Resolv::DNS::Resource::IN::MX
148
+ # * Resolv::DNS::Resource::IN::TXT
149
+ # * Resolv::DNS::Resource::IN::ANY
150
+ # * Resolv::DNS::Resource::IN::A
151
+ # * Resolv::DNS::Resource::IN::WKS
152
+ # * Resolv::DNS::Resource::IN::PTR
153
+ # * Resolv::DNS::Resource::IN::AAAA
154
+
155
+ # Returns a cname or nil
156
+ def cname(host)
157
+ require 'resolv'
158
+ host = host.host if host.kind_of?(URI)
159
+ begin
160
+ resolv = Resolv::DNS.new # { :nameserver => [] }
161
+ resolv.getresources(host, Resolv::DNS::Resource::IN::CNAME).collect { |cname| cname.name.to_s }.first
162
+ rescue => ex
163
+ Stella.ld "Error getting CNAME for #{host}: #{ex.message} (#{ex.class})"
164
+ Stella.ld ex.backtrace
165
+ nil
166
+ end
167
+ end
168
+
169
+ def local_ipaddr?(addr)
170
+ addr = IPAddr.new(addr) if String === addr
171
+ ADDR_LOCAL.include?(addr)
172
+ end
173
+
174
+ def private_ipaddr?(addr)
175
+ addr = IPAddr.new(addr) if String === addr
176
+ ADDR_CLASSA.include?(addr) ||
177
+ ADDR_CLASSB.include?(addr) ||
178
+ ADDR_CLASSC.include?(addr)
179
+ end
180
+
181
+ def ec2_cname_to_ipaddr(cname)
182
+ return unless cname =~ /\Aec2-(\d+)-(\d+)-(\d+)-(\d+)\./
183
+ [$1, $2, $3, $4].join '.'
184
+ end
185
+
186
+ def ec2_ipaddr?(addr)
187
+ ec2_us_east_ipaddr?(addr) || ec2_us_west_ipaddr?(addr) ||
188
+ ec2_eu_west_ipaddr?(addr) || ec2_ap_east_ipaddr?(addr)
189
+ end
190
+
191
+ def ec2_us_east_ipaddr?(addr)
192
+ ADDR_EC2_US_EAST.each { |ipclass| return true if ipclass.include?(addr) }
193
+ false
194
+ end
195
+ def ec2_us_west_ipaddr?(addr)
196
+ ADDR_EC2_US_WEST.each { |ipclass| return true if ipclass.include?(addr) }
197
+ false
198
+ end
199
+ def ec2_eu_west_ipaddr?(addr)
200
+ ADDR_EC2_EU_WEST.each { |ipclass| return true if ipclass.include?(addr) }
201
+ false
202
+ end
203
+ def ec2_ap_east_ipaddr?(addr)
204
+ ADDR_EC2_AP_EAST.each { |ipclass| return true if ipclass.include?(addr) }
205
+ false
206
+ end
207
+
208
+ def hosted_at_ec2?(hostname, region=nil)
209
+ meth = region.nil? ? :ec2_ipaddr? : :"ec2_#{region}_ipaddr?"
210
+ cname = Stella::Utils.cname(hostname)
211
+ if !cname.nil? && cname.first
212
+ addr = Stella::Utils.ec2_cname_to_ipaddr(cname.first)
213
+ else
214
+ addresses = Stella::Utils.ipaddr(hostname) || []
215
+ addr = addresses.first
216
+ end
217
+ addr.nil? ? false : Stella::Utils.send(meth, addr)
218
+ end
219
+
220
+ def valid_hostname?(uri)
221
+ begin
222
+ if String === uri
223
+ uri = "http://#{uri}" unless uri.match(/^https?:\/\//)
224
+ uri = URI.parse(uri)
225
+ end
226
+ hostname = Socket.gethostbyname(uri.host).first
227
+ true
228
+ rescue SocketError => ex
229
+ Stella.ld "#{uri.host}: #{ex.message}"
230
+ false
231
+ end
232
+ end
11
233
 
12
234
  # Return the external IP address (the one seen by the internet)
13
235
  def external_ip_address
@@ -139,5 +361,8 @@ module Stella
139
361
  str.gsub(/^[[:blank:]]{#{indent}}/, '')
140
362
  end
141
363
 
364
+
365
+ IPAddr.new("127.0.0.0/8")
366
+
142
367
  end
143
368
  end
data/stella.gemspec CHANGED
@@ -5,66 +5,68 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stella}
8
- s.version = "0.8.8.001"
8
+ s.version = "2.0.1.001"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Delano Mandelbaum"]
12
- s.date = %q{2011-02-26}
12
+ s.date = %q{2011-06-08}
13
13
  s.default_executable = %q{stella}
14
- s.description = %q{Blame Stella for breaking your web application!}
14
+ s.description = %q{Define realistic testplans and run them against your webapps}
15
15
  s.email = %q{delano@solutious.com}
16
16
  s.executables = ["stella"]
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE.txt",
19
- "README.md"
19
+ "README.md",
20
+ "TODO"
20
21
  ]
21
22
  s.files = [
22
23
  ".gitignore",
23
24
  ".gitmodules",
24
25
  "CHANGES.txt",
26
+ "Gemfile",
27
+ "Gemfile.lock",
25
28
  "LICENSE.txt",
26
29
  "README.md",
27
30
  "Rakefile",
28
31
  "Rudyfile",
32
+ "TODO",
29
33
  "VERSION.yml",
30
34
  "bin/stella",
31
- "examples/cookies/plan.rb",
32
- "examples/csvdata/plan.rb",
33
- "examples/csvdata/search_terms.csv",
34
- "examples/dynamic/plan.rb",
35
- "examples/essentials/logo.png",
36
- "examples/essentials/plan.rb",
37
- "examples/essentials/search_terms.txt",
38
- "examples/exceptions/plan.rb",
39
- "examples/httpauth/plan.rb",
40
- "examples/timeout/plan.rb",
41
- "examples/variables/plan.rb",
35
+ "certs/README.txt",
36
+ "certs/cacerts.pem",
37
+ "certs/gd-class2-root.crt",
38
+ "certs/gd_bundle.crt",
39
+ "certs/gd_intermediate.crt",
40
+ "certs/startssl-ca.pem",
41
+ "certs/startssl-sub.class1.server.ca.pem",
42
+ "certs/stella-master.crt",
42
43
  "lib/stella.rb",
43
44
  "lib/stella/cli.rb",
44
45
  "lib/stella/client.rb",
45
- "lib/stella/client/container.rb",
46
- "lib/stella/common.rb",
47
- "lib/stella/data.rb",
48
- "lib/stella/data/http.rb",
46
+ "lib/stella/core_ext.rb",
49
47
  "lib/stella/engine.rb",
50
- "lib/stella/engine/functional.rb",
51
- "lib/stella/engine/load.rb",
52
- "lib/stella/guidelines.rb",
53
- "lib/stella/logger.rb",
48
+ "lib/stella/report.rb",
54
49
  "lib/stella/testplan.rb",
55
50
  "lib/stella/utils.rb",
56
- "lib/stella/utils/httputil.rb",
57
51
  "stella.gemspec",
58
52
  "support/sample_webapp/app.rb",
59
53
  "support/sample_webapp/config.ru",
60
54
  "support/useragents.txt",
61
- "try/01_numeric_mixins_tryouts.rb",
62
- "try/12_digest_tryouts.rb",
63
- "try/70_module_usage.rb",
64
- "try/api/10_functional.rb",
65
- "try/configs/failed_requests.rb",
66
- "try/configs/global_sequential.rb",
67
- "try/proofs/thread_queue.rb",
55
+ "try/00_basics_try.rb",
56
+ "try/01_selectable_try.rb",
57
+ "try/09_utils_try.rb",
58
+ "try/10_stella_object_try.rb",
59
+ "try/40_report_try.rb",
60
+ "try/90_class_syntax_try.rb",
61
+ "try/emhttp.rb",
62
+ "try/rubyroute.rb",
63
+ "try/support/file.bmp",
64
+ "try/support/file.gif",
65
+ "try/support/file.ico",
66
+ "try/support/file.jpeg",
67
+ "try/support/file.jpg",
68
+ "try/support/file.png",
69
+ "try/traceviz.rb",
68
70
  "vendor/httpclient-2.1.5.2/httpclient.rb",
69
71
  "vendor/httpclient-2.1.5.2/httpclient/auth.rb",
70
72
  "vendor/httpclient-2.1.5.2/httpclient/cacert.p7s",
@@ -77,48 +79,47 @@ Gem::Specification.new do |s|
77
79
  "vendor/httpclient-2.1.5.2/httpclient/timeout.rb",
78
80
  "vendor/httpclient-2.1.5.2/httpclient/util.rb"
79
81
  ]
80
- s.homepage = %q{http://blamestella.com/}
82
+ s.homepage = %q{http://github.com/solutious/stella}
81
83
  s.rdoc_options = ["--charset=UTF-8"]
82
84
  s.require_paths = ["lib"]
83
85
  s.rubyforge_project = %q{stella}
84
86
  s.rubygems_version = %q{1.5.2}
85
- s.summary = %q{Blame Stella for breaking your web application!}
86
- s.test_files = [
87
- "examples/cookies/plan.rb",
88
- "examples/csvdata/plan.rb",
89
- "examples/dynamic/plan.rb",
90
- "examples/essentials/plan.rb",
91
- "examples/exceptions/plan.rb",
92
- "examples/httpauth/plan.rb",
93
- "examples/timeout/plan.rb",
94
- "examples/variables/plan.rb"
95
- ]
87
+ s.summary = %q{Define realistic testplans and run them against your webapps}
96
88
 
97
89
  if s.respond_to? :specification_version then
98
90
  s.specification_version = 3
99
91
 
100
92
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
101
- s.add_runtime_dependency(%q<gibbler>, [">= 0.8.1"])
93
+ s.add_runtime_dependency(%q<familia>, [">= 0.7.1"])
94
+ s.add_runtime_dependency(%q<gibbler>, [">= 0.8.9"])
102
95
  s.add_runtime_dependency(%q<drydock>, [">= 0.6.9"])
103
- s.add_runtime_dependency(%q<benelux>, ["= 0.5.17"])
96
+ s.add_runtime_dependency(%q<benelux>, [">= 0.6.1"])
104
97
  s.add_runtime_dependency(%q<sysinfo>, [">= 0.7.3"])
105
- s.add_runtime_dependency(%q<storable>, [">= 0.7.3"])
106
- s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
98
+ s.add_runtime_dependency(%q<storable>, [">= 0.8.8"])
99
+ s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.4"])
100
+ s.add_runtime_dependency(%q<public_suffix_service>, [">= 0.8.1"])
101
+ s.add_runtime_dependency(%q<whois>, [">= 1.6.6"])
107
102
  else
108
- s.add_dependency(%q<gibbler>, [">= 0.8.1"])
103
+ s.add_dependency(%q<familia>, [">= 0.7.1"])
104
+ s.add_dependency(%q<gibbler>, [">= 0.8.9"])
109
105
  s.add_dependency(%q<drydock>, [">= 0.6.9"])
110
- s.add_dependency(%q<benelux>, ["= 0.5.17"])
106
+ s.add_dependency(%q<benelux>, [">= 0.6.1"])
111
107
  s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
112
- s.add_dependency(%q<storable>, [">= 0.7.3"])
113
- s.add_dependency(%q<nokogiri>, [">= 0"])
108
+ s.add_dependency(%q<storable>, [">= 0.8.8"])
109
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
110
+ s.add_dependency(%q<public_suffix_service>, [">= 0.8.1"])
111
+ s.add_dependency(%q<whois>, [">= 1.6.6"])
114
112
  end
115
113
  else
116
- s.add_dependency(%q<gibbler>, [">= 0.8.1"])
114
+ s.add_dependency(%q<familia>, [">= 0.7.1"])
115
+ s.add_dependency(%q<gibbler>, [">= 0.8.9"])
117
116
  s.add_dependency(%q<drydock>, [">= 0.6.9"])
118
- s.add_dependency(%q<benelux>, ["= 0.5.17"])
117
+ s.add_dependency(%q<benelux>, [">= 0.6.1"])
119
118
  s.add_dependency(%q<sysinfo>, [">= 0.7.3"])
120
- s.add_dependency(%q<storable>, [">= 0.7.3"])
121
- s.add_dependency(%q<nokogiri>, [">= 0"])
119
+ s.add_dependency(%q<storable>, [">= 0.8.8"])
120
+ s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
121
+ s.add_dependency(%q<public_suffix_service>, [">= 0.8.1"])
122
+ s.add_dependency(%q<whois>, [">= 1.6.6"])
122
123
  end
123
124
  end
124
125
 
@@ -0,0 +1,29 @@
1
+ require 'stella'
2
+
3
+ TEST_URI = 'http://bff.heroku.com/'
4
+
5
+ ## Stella::Engine is aware of all available modes
6
+ Stella::Engine.modes
7
+ #=> { :checkup => Stella::Engine::Checkup }
8
+
9
+ ## Checkup has a mode
10
+ Stella::Engine::Checkup.mode
11
+ #=> :checkup
12
+
13
+ ## Can run checkup
14
+ @plan = Stella::Testplan.new TEST_URI
15
+ @run = Stella::Testrun.new @plan, :checkup, :repetitions => 3, :agent => :poop
16
+ Stella::Engine::Checkup.run @run
17
+ @report = @run.report
18
+ @report.processed?
19
+ #=> true
20
+
21
+ ## Knows about errors
22
+ @report.errors?
23
+ #=> false
24
+
25
+ ## Can be yaml
26
+ @report.to_yaml.size > 100
27
+ #=> true
28
+
29
+
@@ -0,0 +1,25 @@
1
+ require 'selectable'
2
+
3
+ ## Can tag Strings
4
+ a = TaggableString.new("stella")
5
+ a.add_tags :type => :metric, :title => "Metric"
6
+ a.tag_values
7
+ #=> [:metric, "Metric"]
8
+
9
+ ## Can filter Array with full tags
10
+ a = SelectableArray.new
11
+ a << TaggableString.new("stella1", :type => :metric)
12
+ a << TaggableString.new("stella2", :type => :metric)
13
+ a << TaggableString.new("stella3", :type => :log)
14
+ a.filter(:type => :metric)
15
+ #=> ["stella1", "stella2"]
16
+
17
+ ## Can filter Array with just values
18
+ a = SelectableArray.new
19
+ a << TaggableString.new("stella1", :type => :metric)
20
+ a << TaggableString.new("stella2", :type => :metric)
21
+ a << TaggableString.new("stella3", :type => :log)
22
+ a.filter(:metric)
23
+ #=> ["stella1", "stella2"]
24
+
25
+