cloudkit 0.11.0 → 0.11.1

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/CHANGES CHANGED
@@ -1,3 +1,10 @@
1
+ 0.11.1
2
+ - Added a block option for configuring OpenID bypassed routes (Devlin Daley)
3
+ - Added write locks for Tokyo Tyrant Tables
4
+ - Added Tokyo Tyrant Table example
5
+ - Fixed POST method tunneling bug (Saimon Moore)
6
+ - Fixed escaping of nested JSON Objects and Arrays
7
+
1
8
  0.11.0
2
9
  - Added Tokyo Cabinet storage
3
10
  - Added MemoryTable development-time storage
data/cloudkit.gemspec CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
2
2
  s.specification_version = 2 if s.respond_to? :specification_version=
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
  s.name = "cloudkit"
5
- s.version = "0.11.0"
6
- s.date = "2008-03-09"
5
+ s.version = "0.11.1"
6
+ s.date = "2008-03-24"
7
7
  s.summary = "An Open Web JSON Appliance."
8
8
  s.description = "An Open Web JSON Appliance."
9
9
  s.authors = ["Jon Crosby"]
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  examples/3.ru
30
30
  examples/4.ru
31
31
  examples/5.ru
32
+ examples/6.ru
32
33
  examples/TOC
33
34
  lib/cloudkit.rb
34
35
  lib/cloudkit/constants.rb
data/doc/curl.html CHANGED
@@ -38,13 +38,13 @@ If you haven't already installed the gem:
38
38
  </p>
39
39
 
40
40
  <p>
41
- If you already have the gem, make sure you're running the latest version (0.11.0):
41
+ If you already have the gem, make sure you're running the latest version (0.11.1):
42
42
  <div class="code">
43
43
  $ gem list cloudkit<br/>
44
- cloudkit (0.9.1) &lt;-- need to upgrade<br/>
44
+ cloudkit (0.10.0) &lt;-- need to upgrade<br/>
45
45
  $ gem update cloudkit<br/>
46
46
  $ gem list cloudkit<br/>
47
- cloudkit (0.11.0, 0.9.1) &lt;-- 0.11.0 is now in the list
47
+ cloudkit (0.11.1, 0.10.0) &lt;-- 0.11.1 is now in the list
48
48
  </div>
49
49
  </p>
50
50
 
data/doc/index.html CHANGED
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
  <div class="meta">
26
26
  <p class="wrapper">
27
- Version 0.11 released with Tokyo Cabinet support. Install with <em>gem install cloudkit</em>.
27
+ Version 0.11.1 released with Tokyo Cabinet support. Install with <em>gem install cloudkit</em>.
28
28
  </p>
29
29
  </div>
30
30
  <div class="wrapper intro-row">
data/examples/6.ru ADDED
@@ -0,0 +1,11 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
2
+ require 'cloudkit'
3
+ require 'rufus/tokyo/tyrant' # gem install rufus-tokyo
4
+ # start Tokyo Tyrant with a table store...
5
+ # ttserver data.tct
6
+ CloudKit.setup_storage_adapter(Rufus::Tokyo::TyrantTable.new('127.0.0.1', 1978))
7
+ use Rack::Session::Pool
8
+ use CloudKit::OAuthFilter
9
+ use CloudKit::OpenIDFilter
10
+ use CloudKit::Service, :collections => [:notes]
11
+ run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
data/examples/TOC CHANGED
@@ -4,7 +4,7 @@ Index of Examples
4
4
  When using the gem version of CloudKit, the first line of each example can be
5
5
  removed.
6
6
 
7
- 1. Expose Notes - Mount a JSON "notes" API. Uses in-memory SQLite.
7
+ 1. Expose Notes - Mount a JSON "notes" API. Uses in-memory store.
8
8
 
9
9
  2. Contain Notes - Same as #1, adding OpenID and OAuth.
10
10
 
@@ -13,3 +13,5 @@ removed.
13
13
  4. Notes with OAuth - Same as #1 using only OAuth.
14
14
 
15
15
  5. Tokyo Notes - Same as #2 with a Tokyo Cabinet Table store.
16
+
17
+ 6. Tyrant Notes - Same as #2 with a Tokyo Tyrant Table store.
data/lib/cloudkit.rb CHANGED
@@ -34,11 +34,11 @@ require 'cloudkit/user_store'
34
34
  include CloudKit::Constants
35
35
 
36
36
  module CloudKit
37
- VERSION = '0.11.0'
37
+ VERSION = '0.11.1'
38
38
 
39
39
  # Sets up the storage adapter. Defaults to development-time
40
40
  # CloudKit::MemoryTable. Also supports Rufus Tokyo Table instances. See the
41
- # examples directory for a demonstration.
41
+ # examples directory for Cabinet and Tyrant Table examples.
42
42
  def self.setup_storage_adapter(adapter_instance=nil)
43
43
  @storage_adapter = adapter_instance || CloudKit::MemoryTable.new
44
44
  end
@@ -19,9 +19,10 @@ module CloudKit
19
19
  @@lock = Mutex.new
20
20
  @@store = nil
21
21
 
22
- def initialize(app, options={})
22
+ def initialize(app, options={}, &bypass_route_callback)
23
23
  @app = app
24
24
  @options = options
25
+ @bypass_route_callback = bypass_route_callback || Proc.new {|url| url == '/'}
25
26
  end
26
27
 
27
28
  def call(env)
@@ -213,12 +214,12 @@ module CloudKit
213
214
  end
214
215
 
215
216
  def allow?(uri)
216
- @options[:allow] && @options[:allow].include?(uri)
217
+ @bypass_route_callback.call(uri) ||
218
+ @options[:allow] && @options[:allow].include?(uri)
217
219
  end
218
220
 
219
221
  def bypass?(request)
220
- root_request?(request) ||
221
- allow?(request.path_info) ||
222
+ allow?(request.path_info) ||
222
223
  valid_auth_key?(request) ||
223
224
  logged_in?(request)
224
225
  end
@@ -17,7 +17,9 @@ module CloudKit
17
17
  # Return the JSON content from the request body
18
18
  def json
19
19
  self.body.rewind
20
- self.body.read
20
+ raw = self.body.read
21
+ # extract the json from the body to avoid tunneled _method param from being parsed as json
22
+ (matches = raw.match(/(\{.*\})/)) ? matches[1] : raw
21
23
  end
22
24
 
23
25
  # Return a CloudKit::URI instance representing the rack request's path info.
@@ -66,7 +66,7 @@ module CloudKit
66
66
 
67
67
  def post(request)
68
68
  if tunnel_methods.include?(request['_method'].try(:upcase))
69
- return send(request['_method'].downcase)
69
+ return send(request['_method'].downcase, request)
70
70
  end
71
71
  @store.post(
72
72
  request.uri,
@@ -59,12 +59,6 @@ module CloudKit
59
59
  q.run(self)
60
60
  end
61
61
 
62
- # Simulate a transaction. This development-mode transaction merely yields
63
- # to its block.
64
- def transaction
65
- yield
66
- end
67
-
68
62
  protected
69
63
 
70
64
  def valid?(record)
@@ -49,7 +49,7 @@ module CloudKit
49
49
  # modify resources that are not current.
50
50
  def update(json, remote_user=nil)
51
51
  raise HistoricalIntegrityViolation unless current?
52
- CloudKit.storage_adapter.transaction do
52
+ transaction do
53
53
  record = CloudKit.storage_adapter[@id]
54
54
  record['uri'] = "#{@uri.string}/versions/#{@etag}"
55
55
  record['archived'] = escape(true)
@@ -65,7 +65,7 @@ module CloudKit
65
65
  # are not current.
66
66
  def delete
67
67
  raise HistoricalIntegrityViolation unless current?
68
- CloudKit.storage_adapter.transaction do
68
+ transaction do
69
69
  original_uri = @uri
70
70
  record = CloudKit.storage_adapter[@id]
71
71
  record['uri'] = "#{@uri.string}/versions/#{@etag}"
@@ -223,6 +223,8 @@ module CloudKit
223
223
  "null"
224
224
  when Fixnum, Bignum, Float
225
225
  value.to_s
226
+ when Array, Hash
227
+ JSON.generate(value) # temporary bug fix prior to JSONQuery support
226
228
  else
227
229
  value
228
230
  end
@@ -252,5 +254,16 @@ module CloudKit
252
254
  def escape_values(hash)
253
255
  hash.inject({}) { |memo, pair| memo.merge({pair[0] => escape(pair[1])}) }
254
256
  end
257
+
258
+ def transaction
259
+ open('.lock', 'w+') do |f|
260
+ f.flock(File::LOCK_EX)
261
+ begin
262
+ yield
263
+ ensure
264
+ f.flock(File::LOCK_UN)
265
+ end
266
+ end
267
+ end
255
268
  end
256
269
  end
@@ -22,6 +22,20 @@ describe "An OpenIDFilter" do
22
22
  response.status.should == 200
23
23
  end
24
24
 
25
+ it "should allow pass through of URIs defined in bypass route callback" do
26
+ openid_app = Rack::Builder.new {
27
+ use Rack::Lint
28
+ use Rack::Session::Pool
29
+ use CloudKit::OpenIDFilter, :allow => ['/foo'] do |url|
30
+ ['/bar'].include? url
31
+ end
32
+ run echo_env(CLOUDKIT_AUTH_KEY)
33
+ }
34
+ request = Rack::MockRequest.new(openid_app)
35
+ response = request.get('/bar')
36
+ response.status.should == 200
37
+ end
38
+
25
39
  it "should redirect to the login page if authorization is required" do
26
40
  response = @request.get('/protected')
27
41
  response.status.should == 302
@@ -61,4 +75,5 @@ describe "An OpenIDFilter" do
61
75
  end
62
76
 
63
77
  end
78
+
64
79
  end
@@ -93,19 +93,38 @@ describe "A Resource" do
93
93
 
94
94
  describe "on create" do
95
95
 
96
- before(:each) do
97
- resource = CloudKit::Resource.create(
96
+ def store_json(hash)
97
+ CloudKit::Resource.create(
98
98
  CloudKit::URI.new('/items/123'),
99
- JSON.generate({:foo => 'bar'}),
99
+ JSON.generate(hash),
100
100
  'http://eric.dolphy.info')
101
- @result = CloudKit.storage_adapter.query { |q|
101
+ CloudKit.storage_adapter.query { |q|
102
102
  q.add_condition 'uri', :eql, '/items/123'
103
103
  }
104
104
  end
105
105
 
106
106
  it "should save the resource" do
107
- @result.size.should == 1
108
- @result.first['json'].should == "{\"foo\":\"bar\"}"
107
+ result = store_json({:foo => 'bar'})
108
+ result.size.should == 1
109
+ result.first['json'].should == "{\"foo\":\"bar\"}"
110
+ end
111
+
112
+ it "should accept nested array values" do
113
+ result = store_json({:foo => [1,2]})
114
+ result.size.should == 1
115
+ result.first['json'].should == '{"foo":[1,2]}'
116
+ end
117
+
118
+ it "should accept nested hash values" do
119
+ result = store_json({:foo => {:bar => 'baz'}})
120
+ result.size.should == 1
121
+ result.first['json'].should == '{"foo":{"bar":"baz"}}'
122
+ end
123
+
124
+ it "should accept recursively nested array/hash values" do
125
+ result = store_json({:foo => [1,{:bar => [2,3]}]})
126
+ result.size.should == 1
127
+ result.first['json'].should == '{"foo":[1,{"bar":[2,3]}]}'
109
128
  end
110
129
 
111
130
  end
data/spec/service_spec.rb CHANGED
@@ -736,6 +736,21 @@ describe "A CloudKit::Service" do
736
736
  new_etag.should_not == etag
737
737
  end
738
738
 
739
+ describe "using POST method tunneling" do
740
+
741
+ it "should behave like a PUT" do
742
+ json = JSON.generate(:this => 'thing')
743
+ response = @request.post(
744
+ '/items/xyz?_method=PUT',
745
+ {:input => json}.merge(VALID_TEST_AUTH))
746
+ response.status.should == 201
747
+ result = @request.get('/items/xyz', VALID_TEST_AUTH)
748
+ result.status.should == 200
749
+ JSON.parse(result.body)['this'].should == 'thing'
750
+ end
751
+
752
+ end
753
+
739
754
  end
740
755
 
741
756
  describe "on DELETE /:collection/:id" do
@@ -848,6 +863,20 @@ describe "A CloudKit::Service" do
848
863
  json['total'].should == 1
849
864
  end
850
865
 
866
+ describe "using POST method tunneling" do
867
+
868
+ it "should behave like a DELETE" do
869
+ response = @request.post(
870
+ '/items/abc?_method=DELETE',
871
+ 'HTTP_IF_MATCH' => @etag,
872
+ CLOUDKIT_AUTH_KEY => TEST_REMOTE_USER)
873
+ response.status.should == 200
874
+ result = @request.get('/items/abc', VALID_TEST_AUTH)
875
+ result.status.should == 410
876
+ end
877
+
878
+ end
879
+
851
880
  end
852
881
 
853
882
  describe "on OPTIONS /:collection" do
@@ -866,6 +895,16 @@ describe "A CloudKit::Service" do
866
895
  methods.sort.should == ['GET', 'HEAD', 'OPTIONS', 'POST']
867
896
  end
868
897
 
898
+ describe "using POST method tunneling" do
899
+
900
+ it "should behave like an OPTIONS request" do
901
+ response = @request.post('/items?_method=OPTIONS', VALID_TEST_AUTH)
902
+ response['Allow'].should_not be_nil
903
+ methods = response['Allow'].split(', ')
904
+ methods.sort.should == ['GET', 'HEAD', 'OPTIONS', 'POST']
905
+ end
906
+ end
907
+
869
908
  end
870
909
 
871
910
  describe "on OPTIONS /:collection/_resolved" do
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.11.0
4
+ version: 0.11.1
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-03-09 00:00:00 -08:00
12
+ date: 2008-03-24 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -90,6 +90,7 @@ files:
90
90
  - examples/3.ru
91
91
  - examples/4.ru
92
92
  - examples/5.ru
93
+ - examples/6.ru
93
94
  - examples/TOC
94
95
  - lib/cloudkit.rb
95
96
  - lib/cloudkit/constants.rb