chronos 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/CHANGELOG.rdoc +27 -0
  2. data/HISTORY.rdoc +4 -0
  3. data/LICENSE.txt +52 -0
  4. data/MANIFEST.txt +51 -0
  5. data/NOTES.rdoc +85 -0
  6. data/README.rdoc +125 -0
  7. data/Rakefile +34 -0
  8. data/TODO.rdoc +63 -0
  9. data/bench/completebench.rb +24 -0
  10. data/ext/cchronos/extconf.rb +5 -0
  11. data/ext/chronos_core/extconf.rb +5 -0
  12. data/lib/chronos.rb +208 -0
  13. data/lib/chronos/calendar.rb +16 -0
  14. data/lib/chronos/calendar/gregorian.rb +94 -0
  15. data/lib/chronos/data/zones.tab +424 -0
  16. data/lib/chronos/datetime.rb +299 -0
  17. data/lib/chronos/datetime/gregorian.rb +698 -0
  18. data/lib/chronos/duration.rb +141 -0
  19. data/lib/chronos/duration/gregorian.rb +261 -0
  20. data/lib/chronos/durationtotext.rb +42 -0
  21. data/lib/chronos/exceptions.rb +16 -0
  22. data/lib/chronos/gregorian.rb +27 -0
  23. data/lib/chronos/interval.rb +132 -0
  24. data/lib/chronos/interval/gregorian.rb +80 -0
  25. data/lib/chronos/locale/parsers/de_CH.rb +50 -0
  26. data/lib/chronos/locale/parsers/en_US.rb +1 -0
  27. data/lib/chronos/locale/parsers/generic.rb +21 -0
  28. data/lib/chronos/locale/strings/de_DE.yaml +76 -0
  29. data/lib/chronos/locale/strings/en_US.yaml +76 -0
  30. data/lib/chronos/minimalistic.rb +37 -0
  31. data/lib/chronos/numeric/gregorian.rb +100 -0
  32. data/lib/chronos/ruby.rb +6 -0
  33. data/lib/chronos/version.rb +21 -0
  34. data/lib/chronos/zone.rb +212 -0
  35. data/rake/initialize.rb +116 -0
  36. data/rake/lib/assesscode.rb +59 -0
  37. data/rake/lib/bonesplitter.rb +245 -0
  38. data/rake/lib/projectclass.rb +69 -0
  39. data/rake/tasks/copyright.rake +24 -0
  40. data/rake/tasks/gem.rake +119 -0
  41. data/rake/tasks/git.rake +40 -0
  42. data/rake/tasks/loc.rake +33 -0
  43. data/rake/tasks/manifest.rake +63 -0
  44. data/rake/tasks/meta.rake +16 -0
  45. data/rake/tasks/notes.rake +36 -0
  46. data/rake/tasks/post_load.rake +18 -0
  47. data/rake/tasks/rdoc.rake +73 -0
  48. data/rake/tasks/rubyforge.rake +67 -0
  49. data/rake/tasks/spec.rake +55 -0
  50. data/spec/bacon_helper.rb +43 -0
  51. data/spec/lib/chronos/datetime/gregorian_spec.rb +314 -0
  52. data/spec/lib/chronos/datetime_spec.rb +219 -0
  53. data/spec/lib/chronos_spec.rb +91 -0
  54. metadata +111 -0
@@ -0,0 +1,59 @@
1
+ #--
2
+ # Copyright 2007-2008 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ class AssessCode
10
+ def initialize(base, *paths)
11
+ @base = base
12
+ @paths = paths
13
+ reset
14
+ end
15
+
16
+ def reset
17
+ @loc_code = 0
18
+ @loc_comment = 0
19
+ @bytes_code = 0
20
+ @bytes_comment = 0
21
+ @bytes_data = 0
22
+ @files = 0
23
+ end
24
+
25
+ def assess
26
+ reset
27
+ @paths.each { |path|
28
+ assess_dir("#{@base}/#{path}")
29
+ }
30
+ end
31
+
32
+ def assess_dir(dir)
33
+ Dir.glob(dir, &method(:assess_file))
34
+ end
35
+
36
+ def assess_file(file)
37
+ comment = false
38
+ @bytes_data += File.size(file)
39
+ @files += 1
40
+ File.readlines(file).each do |line|
41
+ comment = true if line =~ /^=begin/
42
+ comment = false if line =~ /^=end/
43
+ if comment or line =~ /^\s*(?:#|$)/
44
+ @loc_comment += 1
45
+ @bytes_comment += line.strip.size
46
+ else
47
+ @loc_code += 1
48
+ @bytes_code += line.strip.size
49
+ end
50
+ end
51
+ end
52
+
53
+ def put_assessment
54
+ assess
55
+ puts "#{@bytes_data.div(1024)} KB total data in #{@files} files"
56
+ puts "#{@loc_code} Lines of Code (#{@bytes_code.div(1024)} KB)"
57
+ puts "#{@loc_comment} Lines of Comment (#{@bytes_comment.div(1024)} KB)"
58
+ end
59
+ end # class AssessCode
@@ -0,0 +1,245 @@
1
+ #--
2
+ # Copyright 2007-2008 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'stringio'
10
+ require 'ostruct'
11
+
12
+
13
+
14
+ # fix pre-rubygems 1.3 nuisance
15
+ begin
16
+ require 'rubygems'
17
+ module Kernel
18
+ private :gem
19
+ private :require
20
+ private :open
21
+ end
22
+ rescue LoadError; end
23
+
24
+ # needed for has_version?
25
+ class Array
26
+ include Comparable
27
+ end
28
+
29
+ # The module BoneSplitter offers various helpful methods for rake tasks.
30
+ #
31
+ module BoneSplitter
32
+ def self.find_executable(*names)
33
+ path = ENV["PATH"].split(File::PATH_SEPARATOR)
34
+ names.each { |name|
35
+ found = path.map { |path| File.join(path, name) }.find { |e| File.executable?(e) }
36
+ return found if found
37
+ }
38
+ nil
39
+ end
40
+
41
+ @libs = {}
42
+ @bin = OpenStruct.new(
43
+ :diff => find_executable('diff', 'gdiff', 'diff.exe'),
44
+ :sudo => find_executable('sudo'),
45
+ :rcov => find_executable('rcov', 'rcov.bat'),
46
+ :rdoc => find_executable('rdoc', 'rdoc.bat'),
47
+ :gem => find_executable('gem', 'gem.bat', 'gem1.8'),
48
+ :git => find_executable('git')
49
+ )
50
+
51
+ class <<BoneSplitter
52
+ attr_accessor :libs, :bin
53
+ end
54
+
55
+ private
56
+ def optional_task(name, depends_on_constant)
57
+ # puts "#{name} requires #{depends_on_constant}: #{!!deep_const(depends_on_constant)}"
58
+ if deep_const(depends_on_constant) then
59
+ yield
60
+ else
61
+ task name do
62
+ "You're missing a dependency to run this thread (#{depends_on_constant})"
63
+ end
64
+ end
65
+ end
66
+
67
+ def deep_const(name)
68
+ name.split(/::/).inject(Object) { |nesting, name|
69
+ return nil unless nesting.const_defined?(name)
70
+ nesting.const_get(name)
71
+ }
72
+ end
73
+
74
+ def version_proc(constant)
75
+ proc {
76
+ file = constant.gsub(/::/, '/').downcase
77
+ require(file)
78
+ version = deep_const(constant)
79
+ version && version.to_s
80
+ }
81
+ end
82
+
83
+ def quietly
84
+ verbose, $VERBOSE = $VERBOSE, nil
85
+ yield
86
+ ensure
87
+ $VERBOSE = verbose
88
+ end
89
+
90
+ def silenced
91
+ a,b = $stderr, $stdout
92
+ $stderr = StringIO.new
93
+ $stdout = StringIO.new
94
+ yield
95
+ ensure
96
+ $stderr, $stdout = a,b
97
+ end
98
+
99
+ # same as lib? but aborts if a dependency isn't met
100
+ def dependency(names, warn_message=nil)
101
+ abort unless lib?(names, warn_message)
102
+ end
103
+ alias dependencies dependency
104
+
105
+ def lib?(names, warn_message=nil)
106
+ Array(names).map { |name|
107
+ next true if BoneSplitter.libs[name] # already been required earlier
108
+ begin
109
+ silenced do
110
+ require name
111
+ end
112
+ BoneSplitter.libs[name] = true
113
+ true
114
+ rescue LoadError
115
+ warn(warn_message % name) if warn_message
116
+ false
117
+ end
118
+ }.all? # map first so we get all messages at once
119
+ end
120
+
121
+ # Add a lib as present. Use this to fake existence of a lib if you have
122
+ # an in-place substitute for it, like e.g. RDiscount for Markdown.
123
+ def has_lib!(*names)
124
+ names.each { |name|
125
+ BoneSplitter.libs[name] = true
126
+ }
127
+ end
128
+
129
+ def manifest(mani=Project.meta.manifest)
130
+ if File.exist?(mani) then
131
+ File.read(mani).split(/\n/)
132
+ else
133
+ []
134
+ end
135
+ end
136
+
137
+ def bin
138
+ BoneSplitter.bin
139
+ end
140
+
141
+ def manifest_candidates
142
+ cands = Array(Project.manifest.include || '**/*').inject([]) { |a,e| a+Dir[e] }
143
+
144
+ # remove all that are to exclude
145
+ if Project.manifest.exclude then
146
+ Project.manifest.exclude.map { |glob| cands -= Dir[glob] }
147
+ end
148
+ # remove all directories
149
+ cands - Dir['**/*/'].map { |e| e.chop }
150
+ end
151
+
152
+ def has_version?(having_version, minimal_version, maximal_version=nil)
153
+ a = having_version.split(/\./).map { |e| e.to_i }
154
+ b = minimal_version.split(/\./).map { |e| e.to_i }
155
+ c = maximal_version && maximal_version.split(/\./).map { |e| e.to_i }
156
+ c ? a.between?(b,c) : a >= b
157
+ end
158
+
159
+ # requires that 'readme' is a file in markdown format and that Markdown exists
160
+ def extract_summary(file=Project.meta.readme)
161
+ return nil unless File.readable?(file)
162
+ case File.extname(file)
163
+ when '.rdoc'
164
+ File.read('README.rdoc')[/^(\s*=+)\s+SUMMARY\b.*?\n(.*?)\n\1/m, 2]
165
+ when '.markdown'
166
+ return nil unless lib?(%w'hpricot markdown', "Requires %s to extract the summary")
167
+ html = Markdown.new(File.read(file)).to_html
168
+ (Hpricot(html)/"h2[text()=Summary]").first.next_sibling.inner_text.strip
169
+ end
170
+ rescue => e
171
+ warn "Failed extracting the summary: #{e}"
172
+ nil
173
+ end
174
+
175
+ # requires that 'readme' is a file in markdown format and that Markdown exists
176
+ def extract_description(file=Project.meta.readme)
177
+ return nil unless File.readable?(file)
178
+ case File.extname(file)
179
+ when '.rdoc'
180
+ File.read('README.rdoc')[/^(\s*=+)\s+DESCRIPTION\b.*?\n(.*?)\n\1/m, 2]
181
+ when '.markdown'
182
+ return nil unless lib?('hpricot markdown', "Requires %s to extract the summary")
183
+ html = Markdown.new(File.read(file)).to_html
184
+ (Hpricot(html)/"h2[text()=Description]").first.next_sibling.inner_text.strip
185
+ end
186
+ rescue => e
187
+ warn "Failed extracting the description: #{e}"
188
+ nil
189
+ end
190
+
191
+ # Create a Gem::Specification from Project.gem data.
192
+ def gem_spec(from)
193
+ Gem::Specification.new do |s|
194
+ s.name = from.name
195
+ s.version = from.version
196
+ s.summary = from.summary
197
+ s.authors = from.authors
198
+ s.email = from.email
199
+ s.homepage = from.homepage
200
+ s.rubyforge_project = from.rubyforge_project
201
+ s.description = from.description
202
+ s.required_ruby_version = from.required_ruby_version if from.required_ruby_version
203
+
204
+ from.dependencies.each do |dep|
205
+ s.add_dependency(*dep)
206
+ end if from.dependencies
207
+
208
+ s.files = from.files
209
+ s.executables = from.executables.map {|fn| File.basename(fn)}
210
+ s.extensions = from.extensions
211
+
212
+ s.bindir = from.bin_dir
213
+ s.require_paths = from.require_paths if from.require_paths
214
+
215
+ s.rdoc_options = from.rdoc_options
216
+ s.extra_rdoc_files = from.extra_rdoc_files
217
+ s.has_rdoc = from.has_rdoc
218
+
219
+ if from.test_file then
220
+ s.test_file = from.test_file
221
+ elsif from.test_files
222
+ s.test_files = from.test_files
223
+ end
224
+
225
+ # Do any extra stuff the user wants
226
+ from.extras.each do |msg, val|
227
+ case val
228
+ when Proc
229
+ val.call(s.send(msg))
230
+ else
231
+ s.send "#{msg}=", val
232
+ end
233
+ end
234
+ end # Gem::Specification.new
235
+ end
236
+
237
+ # Returns a good name for the gem-file using the spec and the package-name.
238
+ def gem_file(spec, package_name)
239
+ if spec.platform == Gem::Platform::RUBY then
240
+ "#{package_name}.gem"
241
+ else
242
+ "#{package_name}-#{spec.platform}.gem"
243
+ end
244
+ end
245
+ end # BoneSplitter
@@ -0,0 +1,69 @@
1
+ #--
2
+ # Copyright 2007-2008 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ # fix pre-rubygems 1.3 nuisance
10
+ begin
11
+ require 'rubygems'
12
+ module Kernel
13
+ private :gem
14
+ private :require
15
+ private :open
16
+ end
17
+ rescue LoadError; end
18
+
19
+ # FIXME: add a method_added hook to Object and Kernel to keep ProjectClass clean.
20
+
21
+ # This class is not written for long running scripts as it leaks symbols.
22
+ # It is openstructlike, but a bit more lightweight and blankslate so any method will work
23
+ # You can set values to procs and call __finalize__ to get them replaced by the value
24
+ # returned by the proc.
25
+ class ProjectClass
26
+ names = public_instance_methods - %w[initialize inspect __id__ __send__]
27
+ names.each { |m| undef_method m }
28
+
29
+ attr_reader :__hash__
30
+
31
+ def initialize(values=nil)
32
+ @__hash__ = values || {}
33
+ end
34
+
35
+ def [](key)
36
+ @__hash__[key.to_sym]
37
+ end
38
+
39
+ def []=(key,value)
40
+ @__hash__[key.to_sym] = value
41
+ end
42
+
43
+ # All values that respond to .call are replaced by the value
44
+ # returned when calling.
45
+ def __finalize__
46
+ @__hash__.each { |k,v|
47
+ @__hash__[k] = v.call if v.respond_to?(:call)
48
+ }
49
+ end
50
+
51
+ def method_missing(name, *args)
52
+ case args.length
53
+ when 0
54
+ if key = name.to_s[/^(.*)\?$/, 1] then
55
+ !!@__hash__[key.to_sym]
56
+ else
57
+ @__hash__[name]
58
+ end
59
+ when 1
60
+ if key = name.to_s[/^(.*)=$/, 1] then
61
+ @__hash__[key.to_sym] = args.first
62
+ else
63
+ super
64
+ end
65
+ else
66
+ super
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,24 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ namespace :copyright do
10
+ task :update do
11
+ manifest().each { |file|
12
+ data = File.read(file).gsub(/(copyright \d+)(?:(\s*-\s*)\d+)?( by)/i) {
13
+ copyright = "#{$1}#{$2 || '-'}#{Time.now.year}#{$3}"
14
+ puts "#{file}: replacing #{$&.inspect} with #{copyright.inspect}"
15
+ copyright
16
+ }
17
+ File.open(file, "wb") { |fh| fh.write(data) }
18
+ }
19
+ end
20
+ end # namespace :copyright
21
+
22
+ desc 'Alias to copyright:update'
23
+ task :copyright => 'copyright:update'
24
+
@@ -0,0 +1,119 @@
1
+ #--
2
+ # Copyright 2007-2008 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ namespace :gem do
10
+
11
+ # No data in Project
12
+ if Project.gem.nil? then
13
+ task :missing_project do
14
+ abort("No instructions in Project to run the gem tasks")
15
+ end
16
+
17
+ task :package => :missing_project
18
+ task :info => :missing_project
19
+ task :install => :missing_project
20
+ task :uninstall => :missing_project
21
+ task :cleanup => :missing_project
22
+ task :clobber
23
+
24
+ # Rubygems is not installed
25
+ elsif !lib?('rubygems') then
26
+ task :missing_rubygems do
27
+ abort("Requires rubygems to run")
28
+ end
29
+
30
+ task :package => :missing_rubygems
31
+ task :info => :missing_rubygems
32
+ task :install => :missing_rubygems
33
+ task :uninstall => :missing_rubygems
34
+ task :cleanup => :missing_rubygems
35
+ task :clobber
36
+
37
+ # Prerequisites met
38
+ else
39
+ Project.gem.name ||= Project.meta.name
40
+ Project.gem.version ||= Project.meta.version
41
+ Project.gem.summary ||= Project.meta.summary
42
+ Project.gem.description ||= Project.meta.description
43
+ Project.gem.authors ||= Project.meta.authors || Array(Project.meta.author)
44
+ Project.gem.email ||= Project.meta.email
45
+ Project.gem.homepage ||= Project.meta.website
46
+ Project.gem.rubyforge_project ||= (Project.rubyforge && Project.rubyforge.name) || Project.meta.name
47
+ Project.gem.files ||= manifest()
48
+ Project.gem.executables ||= Array(Project.gem.executable)
49
+ Project.gem.extensions ||= Project.gem.files.grep %r/extconf\.rb$/
50
+ Project.gem.bin_dir ||= "bin"
51
+
52
+ Project.gem.rdoc_options ||= Project.rdoc && Project.rdoc.options
53
+ Project.gem.extra_rdoc_files ||= Project.rdoc && Project.rdoc.extra_files
54
+ Project.gem.rdoc_options ||= Project.rdoc && Project.rdoc.options
55
+ Project.gem.__finalize__
56
+ Project.gem.spec = gem_spec(Project.gem)
57
+
58
+ # task gem:package
59
+ dependency 'rake/gempackagetask', 'Requires rake/gempackagetask'
60
+ pkg = Rake::GemPackageTask.new(Project.gem.spec) do |pkg|
61
+ pkg.need_tar = Project.gem.need_tar
62
+ pkg.need_zip = Project.gem.need_zip
63
+ pkg.package_files = Project.gem.files if Project.gem.files
64
+ end
65
+ # Rake::Task['gem:package'].instance_variable_set(:@full_comment, nil)
66
+
67
+ Project.gem.gem_file ||= gem_file(Project.gem.spec, pkg.package_name)
68
+
69
+ desc 'Show information about the gem'
70
+ task :info do
71
+ puts "package_files:"
72
+ puts Project.gem.files
73
+ puts Project.gem.spec.to_ruby
74
+ end
75
+
76
+ desc "Build the gem file #{Project.gem.gem_file}"
77
+ task :package => %W[#{pkg.package_dir}/#{Project.gem.gem_file}]
78
+
79
+ file "#{pkg.package_dir}/#{Project.gem.gem_file}" => [pkg.package_dir, *Project.gem.files] do
80
+ when_writing("Creating GEM") {
81
+ Gem::Builder.new(Project.gem.spec).build
82
+ verbose(true) {
83
+ mv Project.gem.gem_file, "#{pkg.package_dir}/#{Project.gem.gem_file}"
84
+ }
85
+ }
86
+ end
87
+
88
+ desc 'Install the gem'
89
+ task :install => [:clobber, 'gem:package'] do
90
+ sh "#{bin.sudo unless File.writable?(Gem.dir)} #{bin.gem} install --no-update-sources pkg/#{Project.gem.spec.full_name}"
91
+ end
92
+
93
+ desc 'Reinstall the gem'
94
+ task :reinstall => [:uninstall, :install]
95
+
96
+ desc 'Uninstall the gem'
97
+ task :uninstall do
98
+ if installed_list = Gem.source_index.find_name(Project.gem.name) then
99
+ installed_versions = installed_list.map { |s| s.version.to_s }
100
+ if installed_versions.include?(Project.gem.version) then
101
+ sh "#{bin.sudo unless File.writable?(Gem.dir)} #{bin.gem} uninstall --version '#{Project.gem.version}' --ignore-dependencies --executables #{Project.gem.name}"
102
+ end
103
+ end
104
+ end
105
+
106
+ desc 'Cleanup the gem'
107
+ task :cleanup do
108
+ abort("Gem name not set in Project.gem") unless Project.gem.name
109
+ sh "#{bin.sudo unless File.writable?(Gem.dir)} #{bin.gem} cleanup #{Project.gem.name}"
110
+ end
111
+
112
+ task :clobber => :clobber_package
113
+ end
114
+ end # namespace :gem
115
+
116
+ desc 'Alias to gem:package'
117
+ task :gem => 'gem:package'
118
+ task :clobber => 'gem:clobber'
119
+