cloudkit 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +11 -0
- data/README +7 -6
- data/Rakefile +13 -6
- data/TODO +7 -5
- data/cloudkit.gemspec +23 -23
- data/doc/curl.html +2 -2
- data/doc/index.html +4 -6
- data/examples/5.ru +2 -3
- data/examples/TOC +1 -3
- data/lib/cloudkit.rb +17 -10
- data/lib/cloudkit/constants.rb +0 -6
- data/lib/cloudkit/exceptions.rb +10 -0
- data/lib/cloudkit/flash_session.rb +1 -3
- data/lib/cloudkit/oauth_filter.rb +8 -16
- data/lib/cloudkit/oauth_store.rb +9 -13
- data/lib/cloudkit/openid_filter.rb +25 -7
- data/lib/cloudkit/openid_store.rb +14 -17
- data/lib/cloudkit/request.rb +6 -1
- data/lib/cloudkit/service.rb +15 -15
- data/lib/cloudkit/store.rb +97 -284
- data/lib/cloudkit/store/memory_table.rb +105 -0
- data/lib/cloudkit/store/resource.rb +256 -0
- data/lib/cloudkit/store/response_helpers.rb +0 -1
- data/lib/cloudkit/uri.rb +88 -0
- data/lib/cloudkit/user_store.rb +7 -14
- data/spec/ext_spec.rb +76 -0
- data/spec/flash_session_spec.rb +20 -0
- data/spec/memory_table_spec.rb +86 -0
- data/spec/oauth_filter_spec.rb +326 -0
- data/spec/oauth_store_spec.rb +10 -0
- data/spec/openid_filter_spec.rb +64 -0
- data/spec/openid_store_spec.rb +101 -0
- data/spec/rack_builder_spec.rb +39 -0
- data/spec/request_spec.rb +185 -0
- data/spec/resource_spec.rb +291 -0
- data/spec/service_spec.rb +974 -0
- data/{test/helper.rb → spec/spec_helper.rb} +14 -2
- data/spec/store_spec.rb +10 -0
- data/spec/uri_spec.rb +93 -0
- data/spec/user_store_spec.rb +10 -0
- data/spec/util_spec.rb +11 -0
- metadata +37 -61
- data/examples/6.ru +0 -10
- data/lib/cloudkit/store/adapter.rb +0 -8
- data/lib/cloudkit/store/extraction_view.rb +0 -57
- data/lib/cloudkit/store/sql_adapter.rb +0 -36
- data/test/ext_test.rb +0 -76
- data/test/flash_session_test.rb +0 -22
- data/test/oauth_filter_test.rb +0 -331
- data/test/oauth_store_test.rb +0 -12
- data/test/openid_filter_test.rb +0 -60
- data/test/openid_store_test.rb +0 -12
- data/test/rack_builder_test.rb +0 -41
- data/test/request_test.rb +0 -197
- data/test/service_test.rb +0 -971
- data/test/store_test.rb +0 -93
- data/test/user_store_test.rb +0 -12
- data/test/util_test.rb +0 -13
@@ -1,7 +1,5 @@
|
|
1
1
|
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
2
|
require 'cloudkit'
|
3
|
-
require 'test/unit'
|
4
|
-
require 'shoulda'
|
5
3
|
require 'rexml/document'
|
6
4
|
|
7
5
|
TEST_REMOTE_USER = '/cloudkit_users/abcdef'.freeze
|
@@ -18,3 +16,17 @@ end
|
|
18
16
|
def app_headers(content)
|
19
17
|
{'Content-Type' => 'text/html', 'Content-Length' => content.length.to_s}
|
20
18
|
end
|
19
|
+
|
20
|
+
module Rack
|
21
|
+
class Config
|
22
|
+
def initialize(app, &block)
|
23
|
+
@app = app
|
24
|
+
@block = block
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(env)
|
28
|
+
@block.call(env)
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/store_spec.rb
ADDED
data/spec/uri_spec.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "A URI" do
|
4
|
+
|
5
|
+
it "should know its string form" do
|
6
|
+
CloudKit::URI.new('/items/123').string.should == '/items/123'
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should know its parent resource collection" do
|
10
|
+
['/items', '/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
11
|
+
CloudKit::URI.new(uri).collection_uri_fragment.should == '/items'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should split itself into components" do
|
16
|
+
CloudKit::URI.new('/items/123/versions/abc').components.should == ['items', '123', 'versions', 'abc']
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should know its collection type" do
|
20
|
+
['/items', '/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
21
|
+
CloudKit::URI.new(uri).collection_type.should == :items
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should know the URI of its current version" do
|
26
|
+
['/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
27
|
+
CloudKit::URI.new(uri).current_resource_uri.should == '/items/123'
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should know if it is a meta URI" do
|
32
|
+
['/items', '/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
33
|
+
CloudKit::URI.new(uri).should_not be_meta_uri
|
34
|
+
}
|
35
|
+
CloudKit::URI.new('/cloudkit-meta').should be_meta_uri
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should know if it is a resource collection URI" do
|
39
|
+
['/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
40
|
+
CloudKit::URI.new(uri).should_not be_resource_collection_uri
|
41
|
+
}
|
42
|
+
CloudKit::URI.new('/items').should be_resource_collection_uri
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should know if it is a resolved resource collection URI" do
|
46
|
+
['/items', '/items/123', '/items/123/versions', '/items/123/versions/_resolved', '/items/123/versions/abc'].each { |uri|
|
47
|
+
CloudKit::URI.new(uri).should_not be_resolved_resource_collection_uri
|
48
|
+
}
|
49
|
+
CloudKit::URI.new('/items/_resolved').should be_resolved_resource_collection_uri
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should know if it is a resource URI" do
|
53
|
+
['/items', '/items/_resolved', '/items/123/versions', '/items/123/versions/_resolved', '/items/123/versions/abc'].each { |uri|
|
54
|
+
CloudKit::URI.new(uri).should_not be_resource_uri
|
55
|
+
}
|
56
|
+
CloudKit::URI.new('/items/123').should be_resource_uri
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should know if it is a version collection URI" do
|
60
|
+
['/items', '/items/_resolved', '/items/123', '/items/123/versions/_resolved', '/items/123/versions/abc'].each { |uri|
|
61
|
+
CloudKit::URI.new(uri).should_not be_version_collection_uri
|
62
|
+
}
|
63
|
+
CloudKit::URI.new('/items/123/versions').should be_version_collection_uri
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should know if it is a resolved version collection URI" do
|
67
|
+
['/items', '/items/_resolved', '/items/123', '/items/123/versions', '/items/123/versions/abc'].each { |uri|
|
68
|
+
CloudKit::URI.new(uri).should_not be_resolved_version_collection_uri
|
69
|
+
}
|
70
|
+
CloudKit::URI.new('/items/123/versions/_resolved').should be_resolved_version_collection_uri
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should know if it is a resource version URI" do
|
74
|
+
['/items', '/items/_resolved', '/items/123', '/items/123/versions', '/items/123/versions/_resolved'].each { |uri|
|
75
|
+
CloudKit::URI.new(uri).should_not be_resource_version_uri
|
76
|
+
}
|
77
|
+
CloudKit::URI.new('/items/123/versions/abc').should be_resource_version_uri
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should know its cannonical URI string" do
|
81
|
+
CloudKit::URI.new('/items/123').cannonical_uri_string.should == '/items/123'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should generate its cannonical URI string when needed" do
|
85
|
+
CloudKit::URI.new('/items').cannonical_uri_string.should match(/\/items\/.+/)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should fail when attempting to generate a cannonical URI string for versioned resources" do
|
89
|
+
lambda {
|
90
|
+
CloudKit::URI.new('/items/123/versions/abc').cannonical_uri_string
|
91
|
+
}.should raise_error(CloudKit::InvalidURIFormat)
|
92
|
+
end
|
93
|
+
end
|
data/spec/util_spec.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloudkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Crosby
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-03-09 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,16 +22,6 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0.9"
|
24
24
|
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: rack-config
|
27
|
-
type: :runtime
|
28
|
-
version_requirement:
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: "0.9"
|
34
|
-
version:
|
35
25
|
- !ruby/object:Gem::Dependency
|
36
26
|
name: uuid
|
37
27
|
type: :runtime
|
@@ -42,16 +32,6 @@ dependencies:
|
|
42
32
|
- !ruby/object:Gem::Version
|
43
33
|
version: 2.0.1
|
44
34
|
version:
|
45
|
-
- !ruby/object:Gem::Dependency
|
46
|
-
name: sequel
|
47
|
-
type: :runtime
|
48
|
-
version_requirement:
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - ~>
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: "2.9"
|
54
|
-
version:
|
55
35
|
- !ruby/object:Gem::Dependency
|
56
36
|
name: oauth
|
57
37
|
type: :runtime
|
@@ -82,16 +62,6 @@ dependencies:
|
|
82
62
|
- !ruby/object:Gem::Version
|
83
63
|
version: 1.1.3
|
84
64
|
version:
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: sqlite3-ruby
|
87
|
-
type: :runtime
|
88
|
-
version_requirement:
|
89
|
-
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
requirements:
|
91
|
-
- - "="
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: 1.2.4
|
94
|
-
version:
|
95
65
|
description: An Open Web JSON Appliance.
|
96
66
|
email: jon@joncrosby.me
|
97
67
|
executables: []
|
@@ -120,10 +90,10 @@ files:
|
|
120
90
|
- examples/3.ru
|
121
91
|
- examples/4.ru
|
122
92
|
- examples/5.ru
|
123
|
-
- examples/6.ru
|
124
93
|
- examples/TOC
|
125
94
|
- lib/cloudkit.rb
|
126
95
|
- lib/cloudkit/constants.rb
|
96
|
+
- lib/cloudkit/exceptions.rb
|
127
97
|
- lib/cloudkit/flash_session.rb
|
128
98
|
- lib/cloudkit/oauth_filter.rb
|
129
99
|
- lib/cloudkit/oauth_store.rb
|
@@ -134,32 +104,35 @@ files:
|
|
134
104
|
- lib/cloudkit/request.rb
|
135
105
|
- lib/cloudkit/service.rb
|
136
106
|
- lib/cloudkit/store.rb
|
137
|
-
- lib/cloudkit/store/
|
138
|
-
- lib/cloudkit/store/
|
107
|
+
- lib/cloudkit/store/memory_table.rb
|
108
|
+
- lib/cloudkit/store/resource.rb
|
139
109
|
- lib/cloudkit/store/response.rb
|
140
110
|
- lib/cloudkit/store/response_helpers.rb
|
141
|
-
- lib/cloudkit/store/sql_adapter.rb
|
142
111
|
- lib/cloudkit/templates/authorize_request_token.erb
|
143
112
|
- lib/cloudkit/templates/oauth_descriptor.erb
|
144
113
|
- lib/cloudkit/templates/oauth_meta.erb
|
145
114
|
- lib/cloudkit/templates/openid_login.erb
|
146
115
|
- lib/cloudkit/templates/request_authorization.erb
|
147
116
|
- lib/cloudkit/templates/request_token_denied.erb
|
117
|
+
- lib/cloudkit/uri.rb
|
148
118
|
- lib/cloudkit/user_store.rb
|
149
119
|
- lib/cloudkit/util.rb
|
150
|
-
-
|
151
|
-
-
|
152
|
-
-
|
153
|
-
-
|
154
|
-
-
|
155
|
-
-
|
156
|
-
-
|
157
|
-
-
|
158
|
-
-
|
159
|
-
-
|
160
|
-
-
|
161
|
-
-
|
162
|
-
-
|
120
|
+
- spec/ext_spec.rb
|
121
|
+
- spec/flash_session_spec.rb
|
122
|
+
- spec/memory_table_spec.rb
|
123
|
+
- spec/oauth_filter_spec.rb
|
124
|
+
- spec/oauth_store_spec.rb
|
125
|
+
- spec/openid_filter_spec.rb
|
126
|
+
- spec/openid_store_spec.rb
|
127
|
+
- spec/rack_builder_spec.rb
|
128
|
+
- spec/request_spec.rb
|
129
|
+
- spec/resource_spec.rb
|
130
|
+
- spec/service_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/store_spec.rb
|
133
|
+
- spec/uri_spec.rb
|
134
|
+
- spec/user_store_spec.rb
|
135
|
+
- spec/util_spec.rb
|
163
136
|
has_rdoc: false
|
164
137
|
homepage: http://getcloudkit.com
|
165
138
|
post_install_message:
|
@@ -187,15 +160,18 @@ signing_key:
|
|
187
160
|
specification_version: 2
|
188
161
|
summary: An Open Web JSON Appliance.
|
189
162
|
test_files:
|
190
|
-
-
|
191
|
-
-
|
192
|
-
-
|
193
|
-
-
|
194
|
-
-
|
195
|
-
-
|
196
|
-
-
|
197
|
-
-
|
198
|
-
-
|
199
|
-
-
|
200
|
-
-
|
201
|
-
-
|
163
|
+
- spec/ext_spec.rb
|
164
|
+
- spec/flash_session_spec.rb
|
165
|
+
- spec/memory_table_spec.rb
|
166
|
+
- spec/oauth_filter_spec.rb
|
167
|
+
- spec/oauth_store_spec.rb
|
168
|
+
- spec/openid_filter_spec.rb
|
169
|
+
- spec/openid_store_spec.rb
|
170
|
+
- spec/rack_builder_spec.rb
|
171
|
+
- spec/request_spec.rb
|
172
|
+
- spec/resource_spec.rb
|
173
|
+
- spec/service_spec.rb
|
174
|
+
- spec/store_spec.rb
|
175
|
+
- spec/uri_spec.rb
|
176
|
+
- spec/user_store_spec.rb
|
177
|
+
- spec/util_spec.rb
|
data/examples/6.ru
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
-
require 'cloudkit'
|
3
|
-
use Rack::Config do |env|
|
4
|
-
env[CLOUDKIT_STORAGE_URI] = 'mysql://user:pass@localhost/cloudkit_example'
|
5
|
-
end
|
6
|
-
use Rack::Session::Pool
|
7
|
-
use CloudKit::OAuthFilter
|
8
|
-
use CloudKit::OpenIDFilter
|
9
|
-
use CloudKit::Service, :collections => [:notes]
|
10
|
-
run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module CloudKit
|
2
|
-
|
3
|
-
# An ExtractionView observes a resource collection and extracts specified
|
4
|
-
# elements for querying.
|
5
|
-
class ExtractionView
|
6
|
-
attr_accessor :name, :observe, :extract
|
7
|
-
|
8
|
-
# Initialize an ExtractionView.
|
9
|
-
#
|
10
|
-
# ==Examples
|
11
|
-
#
|
12
|
-
# Observe a "notes" collection and extract the titles and colors.
|
13
|
-
# view = ExtractionView.new(
|
14
|
-
# :name => :note_features,
|
15
|
-
# :observe => :notes,
|
16
|
-
# :extract => [:title, :color])
|
17
|
-
#
|
18
|
-
def initialize(name, options)
|
19
|
-
@name = name
|
20
|
-
@observe = options[:observe]
|
21
|
-
@extract = options[:extract]
|
22
|
-
end
|
23
|
-
|
24
|
-
# Map the provided data into a collection for querying.
|
25
|
-
def map(db, collection, uri, data)
|
26
|
-
if @observe == collection
|
27
|
-
elements = @extract.inject({}) do |e, field|
|
28
|
-
e.merge(field.to_s => data[field.to_s])
|
29
|
-
end
|
30
|
-
elements.merge!('uri' => uri)
|
31
|
-
db.transaction do
|
32
|
-
db[@name].filter(:uri => uri).delete
|
33
|
-
db[@name].insert(elements)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Remove the data with the specified URI from the view
|
39
|
-
def unmap(db, type, uri)
|
40
|
-
if @observe == type
|
41
|
-
db[@name].filter(:uri => uri).delete
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Initialize the storage for this view
|
46
|
-
def initialize_storage(db)
|
47
|
-
extractions = @extract
|
48
|
-
db.create_table @name do
|
49
|
-
extractions.each do |field|
|
50
|
-
text field
|
51
|
-
end
|
52
|
-
primary_key :id
|
53
|
-
varchar :uri
|
54
|
-
end unless db.table_exists?(@name)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module CloudKit
|
2
|
-
|
3
|
-
# Adapts a CloudKit::Store to a SQL backend.
|
4
|
-
class SQLAdapter < Adapter
|
5
|
-
|
6
|
-
# Initialize a new SQL backend. Defaults to in-memory SQLite.
|
7
|
-
def initialize(uri=nil, options={})
|
8
|
-
@db = uri ? Sequel.connect(uri, options) : Sequel.sqlite
|
9
|
-
# TODO accept views as part of a store, then initialize them here
|
10
|
-
initialize_storage
|
11
|
-
end
|
12
|
-
|
13
|
-
# method_missing is a placeholder for future interface extraction into
|
14
|
-
# CloudKit::Adapter.
|
15
|
-
def method_missing(method, *args, &block)
|
16
|
-
@db.send(method, *args, &block)
|
17
|
-
end
|
18
|
-
|
19
|
-
protected
|
20
|
-
|
21
|
-
# Initialize the HTTP-oriented storage if it does not exist.
|
22
|
-
def initialize_storage
|
23
|
-
@db.create_table CLOUDKIT_STORE do
|
24
|
-
primary_key :id
|
25
|
-
varchar :uri, :unique => true
|
26
|
-
varchar :etag
|
27
|
-
varchar :collection_reference
|
28
|
-
varchar :resource_reference
|
29
|
-
varchar :last_modified
|
30
|
-
varchar :remote_user
|
31
|
-
text :content
|
32
|
-
boolean :deleted, :default => false
|
33
|
-
end unless @db.table_exists?(CLOUDKIT_STORE)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/test/ext_test.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
class ExtTest < Test::Unit::TestCase
|
3
|
-
|
4
|
-
context "A Hash" do
|
5
|
-
|
6
|
-
should "re-key an entry if it exists" do
|
7
|
-
x = {:a => 1, :b => 2}
|
8
|
-
x.rekey!(:b, :c)
|
9
|
-
assert x == {:a => 1, :c => 2}
|
10
|
-
x.rekey!(:d, :b)
|
11
|
-
assert x == {:a => 1, :c => 2}
|
12
|
-
end
|
13
|
-
|
14
|
-
should "re-key false and nil values" do
|
15
|
-
x = {:a => false, :b => nil}
|
16
|
-
x.rekey!(:b, :c)
|
17
|
-
assert x == {:a => false, :c => nil}
|
18
|
-
x.rekey!(:d, :b)
|
19
|
-
assert x == {:a => false, :c => nil}
|
20
|
-
end
|
21
|
-
|
22
|
-
should "merge conditionally" do
|
23
|
-
x = {:a => 1}
|
24
|
-
y = {:b => 2}
|
25
|
-
x.filter_merge!(:c => y[:c])
|
26
|
-
assert x == {:a => 1}
|
27
|
-
x.filter_merge!(:c => y[:b])
|
28
|
-
assert x == {:a => 1, :c => 2}
|
29
|
-
x = {}.filter_merge!(:a => 1)
|
30
|
-
assert x == {:a => 1}
|
31
|
-
end
|
32
|
-
|
33
|
-
should "merge false values correctly" do
|
34
|
-
x = {:a => 1}
|
35
|
-
y = {:b => 2}
|
36
|
-
x.filter_merge!(:c => false)
|
37
|
-
assert x == {:a => 1, :c => false}
|
38
|
-
x.filter_merge!(:c => y[:b])
|
39
|
-
assert x == {:a => 1, :c => 2}
|
40
|
-
x = {}.filter_merge!(:a => false)
|
41
|
-
assert x == {:a => false}
|
42
|
-
end
|
43
|
-
|
44
|
-
should "exclude pairs using a single key" do
|
45
|
-
x = {:a => 1, :b => 2}
|
46
|
-
y = x.excluding(:b)
|
47
|
-
assert y == {:a => 1}
|
48
|
-
end
|
49
|
-
|
50
|
-
should "exclude pairs using a list of keys" do
|
51
|
-
x = {:a => 1, :b => 2, :c => 3}
|
52
|
-
y = x.excluding(:b, :c)
|
53
|
-
assert y == {:a => 1}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context "An Array" do
|
58
|
-
|
59
|
-
should "exclude elements" do
|
60
|
-
x = [0, 1, 2, 3]
|
61
|
-
y = x.excluding(1, 3)
|
62
|
-
assert_equal [0, 2], y
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context "An Object" do
|
67
|
-
|
68
|
-
should "try" do
|
69
|
-
x = {:a => 'a'}
|
70
|
-
result = x[:a].try(:upcase)
|
71
|
-
assert_equal 'A', result
|
72
|
-
assert_nothing_raised {x[:b].try(:upcase)}
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
end
|