utopia 0.12.5 → 0.12.6

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.
@@ -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