utopia 0.12.5 → 0.12.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,84 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'utopia/extensions/array'
22
+ require 'utopia/extensions/date'
23
+ require 'utopia/extensions/hash'
24
+ require 'utopia/extensions/regexp'
25
+
26
+ module Utopia::ExtensionsSpec
27
+ describe Array do
28
+ it "should split in the middle" do
29
+ a = [1, 2, 3, 4, 5]
30
+
31
+ a, b, c = a.split_at{|x| x == 3}
32
+
33
+ expect(a).to be == [1, 2]
34
+ expect(b).to be == 3
35
+ expect(c).to be == [4, 5]
36
+ end
37
+ end
38
+
39
+ describe Date do
40
+ it "should be equivalent" do
41
+ time = Time.gm(2000) # 2000-01-01 00:00:00 UTC
42
+ date = time.to_date
43
+ datetime = time.to_datetime
44
+
45
+ expect(time <=> date).to be == 0
46
+ expect(time <=> datetime).to be == 0
47
+
48
+ expect(date <=> datetime).to be == 0
49
+ expect(date <=> time).to be == 0
50
+
51
+ expect(datetime <=> time).to be == 0
52
+ expect(datetime <=> date).to be == 0
53
+ end
54
+
55
+ it "should compare correctly" do
56
+ today = Date.today
57
+ yesterday = today - 1
58
+
59
+ expect(today <=> yesterday).to be == 1
60
+ expect(yesterday <=> today).to be == -1
61
+
62
+ expect(today <=> yesterday.to_datetime).to be == 1
63
+ expect(today <=> yesterday.to_time).to be == 1
64
+
65
+ expect(today.to_time <=> yesterday.to_datetime).to be == 1
66
+ expect(today.to_time <=> yesterday.to_time).to be == 1
67
+ end
68
+ end
69
+
70
+ describe Hash do
71
+ it "should symbolize keys" do
72
+ hash = {'a' => 1, 'b' => 2}
73
+
74
+ expect(hash.symbolize_keys).to be == {a: 1, b: 2}
75
+ end
76
+ end
77
+
78
+ describe Regexp do
79
+ it "should start with" do
80
+ expect(Regexp.starts_with("foobar")).to be =~ "foobarbaz"
81
+ expect(Regexp.starts_with("bar")).to_not be =~ "blubarbaz"
82
+ end
83
+ end
84
+ end
@@ -22,24 +22,86 @@ require 'rack/mock'
22
22
  require 'utopia/middleware/controller'
23
23
 
24
24
  module Utopia::Middleware::ControllerSpec
25
+ describe Utopia::Middleware::Controller::Action do
26
+ it "should resolve callbacks" do
27
+ actions = Utopia::Middleware::Controller::Action.new
28
+
29
+ specific_action = actions.define(['a', 'b', 'c']) {puts 'specific_action'}
30
+ indirect_action = actions.define(['**']) {puts 'indirect_action'}
31
+ indirect_named_action = actions.define(['**', 'r']) {puts 'indirect_named_action'}
32
+
33
+ expect(specific_action).to_not be == indirect_action
34
+ expect(indirect_action).to_not be == indirect_named_action
35
+
36
+ expect(actions.select(['a', 'b', 'c'])).to be == [indirect_action, specific_action]
37
+ expect(actions.select(['q'])).to be == [indirect_action]
38
+
39
+ expect(actions.select(['q', 'r'])).to be == [indirect_action, indirect_named_action]
40
+ expect(actions.select(['q', 'r', 's'])).to be == [indirect_action]
41
+ end
42
+
43
+ it "should be greedy matching" do
44
+ actions = Utopia::Middleware::Controller::Action.new
45
+
46
+ greedy_action = actions.define(['**', 'r']) {puts 'greedy_action'}
47
+
48
+ expect(actions.select(['g', 'r'])).to be_include greedy_action
49
+ expect(actions.select(['r'])).to be_include greedy_action
50
+ end
51
+ end
52
+
25
53
  APP = lambda {|env| [404, [], []]}
26
54
 
27
55
  class TestController < Utopia::Middleware::Controller::Base
28
- def direct?(path)
29
- true
30
- end
31
-
32
- def on_success(path, request)
56
+ on 'success' do
33
57
  success!
34
58
  end
35
59
 
36
- def on_failure(path, request)
37
- fail!
60
+ on :failure do
61
+ fail! 400
38
62
  end
39
63
 
40
- def on_variable(path, request)
64
+ on :variable do |request, path|
41
65
  @variable = :value
42
66
  end
67
+
68
+ def self.uri_path
69
+ Utopia::Path["/"]
70
+ end
71
+ end
72
+
73
+ class TestIndirectController < Utopia::Middleware::Controller::Base
74
+ def initialize
75
+ @sequence = ""
76
+ end
77
+
78
+ on('user/update') do
79
+ @sequence << 'A'
80
+ end
81
+
82
+ on('**/comment/post') do
83
+ @sequence << 'B'
84
+ end
85
+
86
+ on('comment/delete') do
87
+ @sequence << 'C'
88
+ end
89
+
90
+ on('**/comment/delete') do
91
+ @sequence << 'D'
92
+ end
93
+
94
+ on('**') do
95
+ @sequence << 'E'
96
+ end
97
+
98
+ on('*') do
99
+ @sequence << 'F'
100
+ end
101
+
102
+ def self.uri_path
103
+ Utopia::Path["/"]
104
+ end
43
105
  end
44
106
 
45
107
  class MockControllerMiddleware
@@ -51,20 +113,52 @@ module Utopia::Middleware::ControllerSpec
51
113
  end
52
114
 
53
115
  describe Utopia::Middleware::Controller do
116
+ let(:variables) {Utopia::Middleware::Controller::Variables.new}
117
+
54
118
  it "should call controller methods" do
55
- variables = Utopia::Middleware::Controller::Variables.new
56
119
  request = Rack::Request.new("utopia.controller" => variables)
57
- middleware = MockControllerMiddleware.new
58
- controller = TestController.new(middleware)
120
+ controller = TestController.new
59
121
 
60
- result = controller.process!(Utopia::Path["/success"], request)
122
+ result = controller.process!(request, Utopia::Path["/success"])
61
123
  expect(result).to be == [200, {}, []]
62
124
 
63
- result = controller.process!(Utopia::Path["/failure"], request)
125
+ result = controller.process!(request, Utopia::Path["/foo/bar/failure"])
64
126
  expect(result).to be == [400, {}, ["Bad Request"]]
65
127
 
66
- result = controller.process!(Utopia::Path["/variable"], request)
128
+ result = controller.process!(request, Utopia::Path["/variable"])
67
129
  expect(variables.to_hash).to be == {"variable"=>:value}
68
130
  end
131
+
132
+ it "should call direct controller methods" do
133
+ request = Rack::Request.new("utopia.controller" => variables)
134
+ controller = TestIndirectController.new
135
+
136
+ controller.process!(request, Utopia::Path["/user/update"])
137
+ expect(variables['sequence']).to be == 'EA'
138
+ end
139
+
140
+ it "should call indirect controller methods" do
141
+ request = Rack::Request.new("utopia.controller" => variables)
142
+ controller = TestIndirectController.new
143
+
144
+ result = controller.process!(request, Utopia::Path["/foo/comment/post"])
145
+ expect(variables['sequence']).to be == 'EB'
146
+ end
147
+
148
+ it "should call multiple indirect controller methods in order" do
149
+ request = Rack::Request.new("utopia.controller" => variables)
150
+ controller = TestIndirectController.new
151
+
152
+ result = controller.process!(request, Utopia::Path["/comment/delete"])
153
+ expect(variables['sequence']).to be == 'EDC'
154
+ end
155
+
156
+ it "should match single patterns" do
157
+ request = Rack::Request.new("utopia.controller" => variables)
158
+ controller = TestIndirectController.new
159
+
160
+ result = controller.process!(request, Utopia::Path["/foo"])
161
+ expect(variables['sequence']).to be == 'EF'
162
+ end
69
163
  end
70
164
  end
@@ -28,5 +28,19 @@ module Utopia::PathSpec
28
28
  expect(root).to be_absolute
29
29
  expect(root + Utopia::Path["foo/bar"]).to be == Utopia::Path["/foo/bar"]
30
30
  end
31
+
32
+ it "should compute all descendant paths" do
33
+ root = Utopia::Path["/foo/bar"]
34
+
35
+ descendants = root.descend.to_a
36
+
37
+ expect(descendants[0].components).to be == [""]
38
+ expect(descendants[1].components).to be == ["", "foo"]
39
+ expect(descendants[2].components).to be == ["", "foo", "bar"]
40
+
41
+ ascendants = root.ascend.to_a
42
+
43
+ expect(descendants.reverse).to be == ascendants
44
+ end
31
45
  end
32
46
  end
@@ -0,0 +1,71 @@
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'utopia/extensions/date'
22
+ require 'utopia/extensions/rack'
23
+
24
+ module Utopia::RackSpec
25
+ describe Rack::Request do
26
+ it "should construct the url" do
27
+ request = Rack::Request.new(
28
+ 'rack.url_scheme' => 'http',
29
+ 'HTTP_HOST' => 'localhost',
30
+ 'SERVER_PORT' => 80
31
+ )
32
+
33
+ expect(request.url_with_path("/foo/bar")).to be == "http://localhost/foo/bar"
34
+ end
35
+ end
36
+
37
+ describe Rack::Response do
38
+ it "should specify not to cache content" do
39
+ response = Rack::Response.new
40
+
41
+ response.cache!(1000)
42
+ response.do_not_cache!
43
+
44
+ expect(response['Cache-Control']).to be == "no-cache, must-revalidate"
45
+
46
+ expires_header = Time.parse(response['Expires'])
47
+ expect(expires_header).to be <= Time.now
48
+ end
49
+
50
+ it "should specify to cache content" do
51
+ response = Rack::Response.new
52
+
53
+ duration = 120
54
+ expires = Time.now + 100 # At least this far into the future
55
+ response.cache!(duration)
56
+
57
+ expect(response['Cache-Control']).to be == "public, max-age=120"
58
+
59
+ expires_header = Time.parse(response['Expires'])
60
+ expect(expires_header).to be >= expires
61
+ end
62
+
63
+ it "should set content type" do
64
+ response = Rack::Response.new
65
+
66
+ response.content_type! "text/html"
67
+
68
+ expect(response['Content-Type']).to be == "text/html"
69
+ end
70
+ end
71
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utopia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.5
4
+ version: 0.12.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-15 00:00:00.000000000 Z
11
+ date: 2014-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trenni
@@ -157,7 +157,6 @@ files:
157
157
  - lib/utopia/extensions/array.rb
158
158
  - lib/utopia/extensions/date.rb
159
159
  - lib/utopia/extensions/hash.rb
160
- - lib/utopia/extensions/maybe.rb
161
160
  - lib/utopia/extensions/rack.rb
162
161
  - lib/utopia/extensions/regexp.rb
163
162
  - lib/utopia/extensions/string.rb
@@ -169,10 +168,13 @@ files:
169
168
  - lib/utopia/middleware/content/node.rb
170
169
  - lib/utopia/middleware/content/processor.rb
171
170
  - lib/utopia/middleware/controller.rb
171
+ - lib/utopia/middleware/controller/action.rb
172
+ - lib/utopia/middleware/controller/base.rb
173
+ - lib/utopia/middleware/controller/variables.rb
172
174
  - lib/utopia/middleware/directory_index.rb
175
+ - lib/utopia/middleware/exception_handler.rb
173
176
  - lib/utopia/middleware/localization.rb
174
177
  - lib/utopia/middleware/localization/name.rb
175
- - lib/utopia/middleware/logger.rb
176
178
  - lib/utopia/middleware/mail_exceptions.rb
177
179
  - lib/utopia/middleware/redirector.rb
178
180
  - lib/utopia/middleware/requester.rb
@@ -181,7 +183,6 @@ files:
181
183
  - lib/utopia/session/encrypted_cookie.rb
182
184
  - lib/utopia/setup.rb
183
185
  - lib/utopia/setup/Gemfile
184
- - lib/utopia/setup/access_log/readme.txt
185
186
  - lib/utopia/setup/cache/head/readme.txt
186
187
  - lib/utopia/setup/cache/meta/readme.txt
187
188
  - lib/utopia/setup/config.ru
@@ -203,13 +204,14 @@ files:
203
204
  - lib/utopia/tags/environment.rb
204
205
  - lib/utopia/tags/node.rb
205
206
  - lib/utopia/tags/override.rb
206
- - lib/utopia/time_store.rb
207
207
  - lib/utopia/version.rb
208
+ - spec/utopia/extensions_spec.rb
208
209
  - spec/utopia/middleware/content_root/_heading.xnode
209
210
  - spec/utopia/middleware/content_root/index.xnode
210
211
  - spec/utopia/middleware/content_spec.rb
211
212
  - spec/utopia/middleware/controller_spec.rb
212
213
  - spec/utopia/path_spec.rb
214
+ - spec/utopia/rack_spec.rb
213
215
  - utopia.gemspec
214
216
  homepage: https://github.com/ioquatix/utopia
215
217
  licenses: []
@@ -235,8 +237,10 @@ signing_key:
235
237
  specification_version: 4
236
238
  summary: Utopia is a framework for building dynamic content-driven websites.
237
239
  test_files:
240
+ - spec/utopia/extensions_spec.rb
238
241
  - spec/utopia/middleware/content_root/_heading.xnode
239
242
  - spec/utopia/middleware/content_root/index.xnode
240
243
  - spec/utopia/middleware/content_spec.rb
241
244
  - spec/utopia/middleware/controller_spec.rb
242
245
  - spec/utopia/path_spec.rb
246
+ - spec/utopia/rack_spec.rb
@@ -1,31 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- class NilClass
22
- def maybe?
23
- end
24
- end
25
-
26
- class Object
27
- # A helper that allows you to avoid excessive number of i
28
- def maybe?
29
- yield self
30
- end
31
- end
@@ -1,78 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
- require 'utopia/time_store'
23
-
24
- module Utopia
25
- module Middleware
26
-
27
- class Logger
28
- ACCESS_LOG = "access_log"
29
- HEADER = [:ip, :agent, :method, :url, :status, :location, :referer, :length]
30
-
31
- def write_log(env, response)
32
- request = Rack::Request.new(env)
33
-
34
- record = {
35
- :ip => request.ip,
36
- :host => request.host,
37
- :url => request.url,
38
- :referer => request.referer,
39
- :agent => env['HTTP_USER_AGENT'],
40
- :status => response[0],
41
- :method => request.request_method,
42
- :user => env['REMOTE_USER'],
43
- :version => env['HTTP_VERSION']
44
- }
45
-
46
- if response[1].key? "Location"
47
- record[:location] = response[1]["Location"]
48
- end
49
-
50
- if response[1].key? "Content-Length"
51
- record[:length] = response[1]["Content-Length"]
52
- end
53
-
54
- @log << record
55
-
56
- if UTOPIA_ENV != :production
57
- $stderr.puts ">> #{record[:method]} #{record[:url]} -> #{response[0]}"
58
- end
59
- end
60
-
61
- def initialize(app, options = {})
62
- @app = app
63
-
64
- @log = options[:log] || TimeStore.new(options[:path] || ACCESS_LOG, options[:header] || HEADER)
65
- end
66
-
67
- def call(env)
68
- response = @app.call(env)
69
-
70
- Thread.new do
71
- write_log(env, response)
72
- end
73
-
74
- return response
75
- end
76
- end
77
- end
78
- end
@@ -1 +0,0 @@
1
- The access log keeps track of all requests and is rotated automatically on a weekly basis.
@@ -1,107 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'set'
22
- require 'csv'
23
-
24
- module Utopia
25
-
26
- # TimeStore is a very simple time oriented database. New entries
27
- # are added in chronological order and it is not possible to change
28
- # this behaviour, or remove old entries. It stores data in a CSV
29
- # format into a directory where each file represents a week in the
30
- # year.
31
- #
32
- # The design of this class is to enable efficient logging of data
33
- # in a backup friendly file format (i.e. files older than one week
34
- # are not touched).
35
- #
36
- # Due to the nature of CSV data, a header must be specified. This
37
- # header can have columns added, but not removed. Columns not
38
- # specified in the header will not be recorded.
39
- #
40
- class TimeStore
41
- def initialize(path, header)
42
- @path = path
43
-
44
- header = header.collect{|name| name.to_s}
45
-
46
- @header_path = File.join(@path, "header.csv")
47
-
48
- if File.exist? @header_path
49
- @header = File.read(@header_path).split(",")
50
- else
51
- @header = []
52
- end
53
-
54
- diff = (Set.new(header) + ["time"]) - @header
55
-
56
- if diff.size
57
- @header += diff.to_a.sort
58
-
59
- File.open(@header_path, "w") do |file|
60
- file.write(@header.join(","))
61
- end
62
- end
63
-
64
- @last_path = nil
65
- @last_file = nil
66
- end
67
-
68
- attr :header
69
-
70
- def path_for_time(time)
71
- return File.join(@path, time.strftime("%Y-%W") + ".csv")
72
- end
73
-
74
- def open(time, &block)
75
- path = path_for_time(time)
76
-
77
- if @last_path != path
78
- if @last_file
79
- @last_file.close
80
- @last_file = nil
81
- end
82
-
83
- @last_file = File.open(path, "a")
84
- @last_file.sync = true
85
- @last_path = path
86
- end
87
-
88
- yield @last_file
89
-
90
- #File.open(path_for_time(time), "a", &block)
91
- end
92
-
93
- def dump(values)
94
- row = @header.collect{|key| values[key.to_sym]}
95
- return CSV.generate_line(row)
96
- end
97
-
98
- def <<(values)
99
- time = values[:time] = Time.now
100
-
101
- open(time) do |file|
102
- file.puts(dump(values))
103
- end
104
- end
105
- end
106
-
107
- end