chicagowarehouse 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +18 -0
  4. data/LICENSE +20 -0
  5. data/README +11 -0
  6. data/Rakefile +50 -0
  7. data/chicagowarehouse.gemspec +134 -0
  8. data/lib/chicago.rb +32 -0
  9. data/lib/chicago/core_ext/hash.rb +18 -0
  10. data/lib/chicago/core_ext/sequel/dataset.rb +7 -0
  11. data/lib/chicago/core_ext/sequel/sql.rb +62 -0
  12. data/lib/chicago/data/month.rb +98 -0
  13. data/lib/chicago/database/constants.rb +18 -0
  14. data/lib/chicago/database/dataset_builder.rb +75 -0
  15. data/lib/chicago/database/filter.rb +109 -0
  16. data/lib/chicago/database/migration_file_writer.rb +34 -0
  17. data/lib/chicago/database/schema_generator.rb +117 -0
  18. data/lib/chicago/database/type_converters.rb +107 -0
  19. data/lib/chicago/database/value_parser.rb +23 -0
  20. data/lib/chicago/errors.rb +23 -0
  21. data/lib/chicago/query.rb +109 -0
  22. data/lib/chicago/rake_tasks.rb +50 -0
  23. data/lib/chicago/schema/builders/column_builder.rb +21 -0
  24. data/lib/chicago/schema/builders/dimension_builder.rb +69 -0
  25. data/lib/chicago/schema/builders/fact_builder.rb +74 -0
  26. data/lib/chicago/schema/builders/shrunken_dimension_builder.rb +54 -0
  27. data/lib/chicago/schema/builders/table_builder.rb +33 -0
  28. data/lib/chicago/schema/column.rb +221 -0
  29. data/lib/chicago/schema/column_parser.rb +127 -0
  30. data/lib/chicago/schema/dimension.rb +129 -0
  31. data/lib/chicago/schema/dimension_reference.rb +47 -0
  32. data/lib/chicago/schema/fact.rb +70 -0
  33. data/lib/chicago/schema/measure.rb +35 -0
  34. data/lib/chicago/schema/named_element.rb +16 -0
  35. data/lib/chicago/schema/named_element_collection.rb +64 -0
  36. data/lib/chicago/schema/query_column.rb +199 -0
  37. data/lib/chicago/schema/table.rb +41 -0
  38. data/lib/chicago/star_schema.rb +127 -0
  39. data/spec/core_ext/sequel_extensions_spec.rb +29 -0
  40. data/spec/data/month_spec.rb +67 -0
  41. data/spec/database/db_type_converter_spec.rb +125 -0
  42. data/spec/database/migration_file_writer_spec.rb +37 -0
  43. data/spec/database/schema_generator_spec.rb +199 -0
  44. data/spec/db_connections.yml.dist +4 -0
  45. data/spec/query_spec.rb +495 -0
  46. data/spec/schema/column_spec.rb +213 -0
  47. data/spec/schema/dimension_builder_spec.rb +32 -0
  48. data/spec/schema/dimension_reference_spec.rb +90 -0
  49. data/spec/schema/dimension_spec.rb +111 -0
  50. data/spec/schema/fact_spec.rb +83 -0
  51. data/spec/schema/measure_spec.rb +27 -0
  52. data/spec/schema/named_element_collection_spec.rb +67 -0
  53. data/spec/schema/pivoted_column_spec.rb +17 -0
  54. data/spec/schema/query_column_spec.rb +120 -0
  55. data/spec/spec_helper.rb +20 -0
  56. data/spec/star_schema_spec.rb +219 -0
  57. data/spec/support/matchers/be_one_of.rb +11 -0
  58. data/spec/support/matchers/column_matchers.rb +11 -0
  59. data/spec/support/shared_examples/column.rb +13 -0
  60. data/spec/support/shared_examples/schema_table.rb +17 -0
  61. data/spec/support/shared_examples/schema_visitor.rb +25 -0
  62. data/tasks/stats.rake +108 -0
  63. metadata +300 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+
3
+ # The distinct extension won't work in Sequel 3.30 onwards at present.
4
+ gem "sequel", "~> 3.0"
5
+ gem "sequel_migration_builder", ">= 0.3.2"
6
+ gem "mysql", "2.8.1"
7
+ gem "chronic"
8
+
9
+ group :development do
10
+ gem "yard"
11
+ gem "rspec", "~> 2.0"
12
+ gem "bundler"
13
+ gem "jeweler"
14
+ gem "rcov"
15
+ gem "flog"
16
+ gem "ZenTest"
17
+ gem "timecop"
18
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Roland Swingler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,11 @@
1
+ Chicago
2
+ =======
3
+
4
+ Chicago is a small-scale data warehouse implementation written in Ruby.
5
+
6
+ Contributing
7
+ ============
8
+
9
+ To get specs passing, you'll need to create a test myqsl database, and
10
+ copy spec/database.yml.dist to spec/database.yml and populate it
11
+ appropriately.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ gem.name = "chicagowarehouse"
17
+ gem.version = "0.3.1"
18
+ gem.summary = "Ruby Data Warehousing"
19
+ gem.description = "Simple Data Warehouse toolkit for ruby"
20
+ gem.author = "Roland Swingler"
21
+ gem.email = "roland.swingler@gmail.com"
22
+ gem.has_rdoc = true
23
+ gem.license = "MIT"
24
+ gem.homepage = "http://github.com/notonthehighstreet/chicago"
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = FileList['spec/**/*_spec.rb']
36
+ spec.rcov = true
37
+ spec.rcov_opts = "-x spec/ -x /home"
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'yard'
43
+ YARD::Rake::YardocTask.new
44
+
45
+ desc "Flog this baby!"
46
+ task :flog do
47
+ sh 'find lib -name "*.rb" | xargs flog -m'
48
+ end
49
+
50
+ load 'tasks/stats.rake'
@@ -0,0 +1,134 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{chicagowarehouse}
8
+ s.version = "0.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Roland Swingler}]
12
+ s.date = %q{2012-10-22}
13
+ s.description = %q{Simple Data Warehouse toolkit for ruby}
14
+ s.email = %q{roland.swingler@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "LICENSE",
24
+ "README",
25
+ "Rakefile",
26
+ "chicagowarehouse.gemspec",
27
+ "lib/chicago.rb",
28
+ "lib/chicago/core_ext/hash.rb",
29
+ "lib/chicago/core_ext/sequel/dataset.rb",
30
+ "lib/chicago/core_ext/sequel/sql.rb",
31
+ "lib/chicago/data/month.rb",
32
+ "lib/chicago/database/constants.rb",
33
+ "lib/chicago/database/dataset_builder.rb",
34
+ "lib/chicago/database/filter.rb",
35
+ "lib/chicago/database/migration_file_writer.rb",
36
+ "lib/chicago/database/schema_generator.rb",
37
+ "lib/chicago/database/type_converters.rb",
38
+ "lib/chicago/database/value_parser.rb",
39
+ "lib/chicago/errors.rb",
40
+ "lib/chicago/query.rb",
41
+ "lib/chicago/rake_tasks.rb",
42
+ "lib/chicago/schema/builders/column_builder.rb",
43
+ "lib/chicago/schema/builders/dimension_builder.rb",
44
+ "lib/chicago/schema/builders/fact_builder.rb",
45
+ "lib/chicago/schema/builders/shrunken_dimension_builder.rb",
46
+ "lib/chicago/schema/builders/table_builder.rb",
47
+ "lib/chicago/schema/column.rb",
48
+ "lib/chicago/schema/column_parser.rb",
49
+ "lib/chicago/schema/dimension.rb",
50
+ "lib/chicago/schema/dimension_reference.rb",
51
+ "lib/chicago/schema/fact.rb",
52
+ "lib/chicago/schema/measure.rb",
53
+ "lib/chicago/schema/named_element.rb",
54
+ "lib/chicago/schema/named_element_collection.rb",
55
+ "lib/chicago/schema/query_column.rb",
56
+ "lib/chicago/schema/table.rb",
57
+ "lib/chicago/star_schema.rb",
58
+ "spec/core_ext/sequel_extensions_spec.rb",
59
+ "spec/data/month_spec.rb",
60
+ "spec/database/db_type_converter_spec.rb",
61
+ "spec/database/migration_file_writer_spec.rb",
62
+ "spec/database/schema_generator_spec.rb",
63
+ "spec/db_connections.yml.dist",
64
+ "spec/query_spec.rb",
65
+ "spec/schema/column_spec.rb",
66
+ "spec/schema/dimension_builder_spec.rb",
67
+ "spec/schema/dimension_reference_spec.rb",
68
+ "spec/schema/dimension_spec.rb",
69
+ "spec/schema/fact_spec.rb",
70
+ "spec/schema/measure_spec.rb",
71
+ "spec/schema/named_element_collection_spec.rb",
72
+ "spec/schema/pivoted_column_spec.rb",
73
+ "spec/schema/query_column_spec.rb",
74
+ "spec/spec_helper.rb",
75
+ "spec/star_schema_spec.rb",
76
+ "spec/support/matchers/be_one_of.rb",
77
+ "spec/support/matchers/column_matchers.rb",
78
+ "spec/support/shared_examples/column.rb",
79
+ "spec/support/shared_examples/schema_table.rb",
80
+ "spec/support/shared_examples/schema_visitor.rb",
81
+ "tasks/stats.rake"
82
+ ]
83
+ s.homepage = %q{http://github.com/notonthehighstreet/chicago}
84
+ s.licenses = [%q{MIT}]
85
+ s.require_paths = [%q{lib}]
86
+ s.rubygems_version = %q{1.8.6}
87
+ s.summary = %q{Ruby Data Warehousing}
88
+
89
+ if s.respond_to? :specification_version then
90
+ s.specification_version = 3
91
+
92
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
93
+ s.add_runtime_dependency(%q<sequel>, ["~> 3.0"])
94
+ s.add_runtime_dependency(%q<sequel_migration_builder>, [">= 0.3.2"])
95
+ s.add_runtime_dependency(%q<mysql>, ["= 2.8.1"])
96
+ s.add_runtime_dependency(%q<chronic>, [">= 0"])
97
+ s.add_development_dependency(%q<yard>, [">= 0"])
98
+ s.add_development_dependency(%q<rspec>, ["~> 2.0"])
99
+ s.add_development_dependency(%q<bundler>, [">= 0"])
100
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
101
+ s.add_development_dependency(%q<rcov>, [">= 0"])
102
+ s.add_development_dependency(%q<flog>, [">= 0"])
103
+ s.add_development_dependency(%q<ZenTest>, [">= 0"])
104
+ s.add_development_dependency(%q<timecop>, [">= 0"])
105
+ else
106
+ s.add_dependency(%q<sequel>, ["~> 3.0"])
107
+ s.add_dependency(%q<sequel_migration_builder>, [">= 0.3.2"])
108
+ s.add_dependency(%q<mysql>, ["= 2.8.1"])
109
+ s.add_dependency(%q<chronic>, [">= 0"])
110
+ s.add_dependency(%q<yard>, [">= 0"])
111
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
112
+ s.add_dependency(%q<bundler>, [">= 0"])
113
+ s.add_dependency(%q<jeweler>, [">= 0"])
114
+ s.add_dependency(%q<rcov>, [">= 0"])
115
+ s.add_dependency(%q<flog>, [">= 0"])
116
+ s.add_dependency(%q<ZenTest>, [">= 0"])
117
+ s.add_dependency(%q<timecop>, [">= 0"])
118
+ end
119
+ else
120
+ s.add_dependency(%q<sequel>, ["~> 3.0"])
121
+ s.add_dependency(%q<sequel_migration_builder>, [">= 0.3.2"])
122
+ s.add_dependency(%q<mysql>, ["= 2.8.1"])
123
+ s.add_dependency(%q<chronic>, [">= 0"])
124
+ s.add_dependency(%q<yard>, [">= 0"])
125
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
126
+ s.add_dependency(%q<bundler>, [">= 0"])
127
+ s.add_dependency(%q<jeweler>, [">= 0"])
128
+ s.add_dependency(%q<rcov>, [">= 0"])
129
+ s.add_dependency(%q<flog>, [">= 0"])
130
+ s.add_dependency(%q<ZenTest>, [">= 0"])
131
+ s.add_dependency(%q<timecop>, [">= 0"])
132
+ end
133
+ end
134
+
data/lib/chicago.rb ADDED
@@ -0,0 +1,32 @@
1
+ # Requires go here
2
+ require 'chronic'
3
+ require 'sequel'
4
+ require 'sequel/extensions/inflector'
5
+
6
+ # TODO: move this back to the Sequel MySQL adapter
7
+ require 'chicago/core_ext/sequel/dataset'
8
+ require 'chicago/core_ext/sequel/sql'
9
+
10
+ require 'chicago/core_ext/hash'
11
+ require 'chicago/data/month'
12
+
13
+ require 'chicago/star_schema'
14
+ require 'chicago/database/constants'
15
+ require 'chicago/database/type_converters'
16
+ require 'chicago/database/migration_file_writer'
17
+ require 'chicago/database/schema_generator'
18
+ require 'chicago/query'
19
+
20
+ module Chicago
21
+ class << self
22
+ # The root directory for the project.
23
+ attr_accessor :project_root
24
+ end
25
+
26
+ # @api private
27
+ module Database
28
+ end
29
+
30
+ ### Autoloads
31
+ autoload :RakeTasks, 'chicago/rake_tasks'
32
+ end
@@ -0,0 +1,18 @@
1
+ class Hash
2
+ # Ensure AcitveSupport related methods are present if ActiveSupport
3
+ # not loaded.
4
+ #
5
+ # Code taken from ActiveSupport.
6
+ unless method_defined?(:symbolize_keys)
7
+ def symbolize_keys
8
+ dup.symbolize_keys!
9
+ end
10
+
11
+ def symbolize_keys!
12
+ keys.each do |key|
13
+ self[(key.to_sym rescue key) || key] = delete(key)
14
+ end
15
+ self
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module Sequel
2
+ class Dataset
3
+ def insert_replace
4
+ clone(:replace => true)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,62 @@
1
+ module Sequel
2
+ # @private
3
+ class Dataset
4
+ COLUMN_DISTINCT = "DISTINCT ".freeze
5
+
6
+ def distinct_expression_sql(e)
7
+ s = ""
8
+ distinct_expression_sql_append(s, e)
9
+ s
10
+ end
11
+
12
+ def distinct_expression_sql_append(sql, e)
13
+ sql << COLUMN_DISTINCT
14
+ literal_append(sql, e.expression)
15
+ end
16
+
17
+ # Provide support for Sequel versions before 3.30.0
18
+ unless method_defined?(:literal_append)
19
+ def literal_append(sql, expression)
20
+ sql << literal(expression)
21
+ end
22
+ end
23
+ end
24
+
25
+ module SQL
26
+ class DistinctExpression < Expression # :nodoc:
27
+ attr_reader :expression
28
+
29
+ def initialize(expression)
30
+ @expression = expression
31
+ end
32
+
33
+ to_s_method :distinct_expression_sql
34
+ end
35
+
36
+ module DistinctMethods
37
+ def distinct
38
+ DistinctExpression.new(self)
39
+ end
40
+ end
41
+
42
+ # @private
43
+ class ComplexExpression
44
+ include DistinctMethods
45
+ end
46
+
47
+ # @private
48
+ class GenericExpression
49
+ include DistinctMethods
50
+ end
51
+ end
52
+ end
53
+
54
+ # @private
55
+ class Symbol
56
+ include Sequel::SQL::DistinctMethods
57
+ end
58
+
59
+ # @private
60
+ class String
61
+ include Sequel::SQL::DistinctMethods
62
+ end
@@ -0,0 +1,98 @@
1
+ module Chicago
2
+ module Data
3
+ # A month of the year.
4
+ #
5
+ # Months cannot be initialized. Instead call the month name:
6
+ #
7
+ # Chicago::Data::Month.march # => returns a month
8
+ #
9
+ # or parse a string or a month number:
10
+ #
11
+ # Chicago::Data::Month.parse("apr") # => returns April
12
+ # Chicago::Data::Month.parse("september") # => returns September
13
+ # Chicago::Data::Month.parse(1) # => returns January
14
+ #
15
+ class Month
16
+ include Comparable
17
+
18
+ # Returns the full English name of this month
19
+ attr_reader :name
20
+
21
+ # Returns the first three letters of the English name of this month.
22
+ attr_reader :short_name
23
+
24
+ # @private
25
+ def initialize(name, number)
26
+ @name = name
27
+ @short_name = name[0..2]
28
+ @number = number
29
+ end
30
+
31
+ # Months are ordered.
32
+ def <=>(other)
33
+ to_i <=> other.to_i
34
+ end
35
+
36
+ # Returns the first day of this month in the given year as a Date.
37
+ def in(year)
38
+ Date.new(year, to_i, 1)
39
+ end
40
+
41
+ # Returns the number of this month from 1 to 12.
42
+ def to_i
43
+ @number
44
+ end
45
+
46
+ # Returns the full name of this month.
47
+ def to_s
48
+ name
49
+ end
50
+
51
+ # All twelve months.
52
+ ALL = [Month.new("January", 1),
53
+ Month.new("February", 2),
54
+ Month.new("March", 3),
55
+ Month.new("April", 4),
56
+ Month.new("May", 5),
57
+ Month.new("June", 6),
58
+ Month.new("July", 7),
59
+ Month.new("August", 8),
60
+ Month.new("September", 9),
61
+ Month.new("October", 10),
62
+ Month.new("November", 11),
63
+ Month.new("December", 12)].freeze
64
+
65
+ class << self
66
+ [:january, :february, :march, :april, :may, :june, :july, :august, :september, :october, :november, :december].each_with_index do |month, i|
67
+ eval "def #{month} ; ALL[#{i}] ; end"
68
+ end
69
+
70
+ # Returns a month given a name of the month or an integer
71
+ # between 1 and 12.
72
+ def parse(identifier)
73
+ if is_month_number?(identifier)
74
+ ALL[identifier - 1]
75
+ else
76
+ find_month_by_name(identifier)
77
+ end
78
+ end
79
+
80
+ def is_month_number?(identifier)
81
+ identifier.kind_of?(Fixnum) && identifier >= 1 && identifier <= 12
82
+ end
83
+ private :is_month_number?
84
+
85
+ def find_month_by_name(identifier)
86
+ name = identifier.strip.downcase
87
+ ALL.find do |month|
88
+ month.name.downcase == name || month.short_name.downcase == name
89
+ end
90
+ end
91
+ private :find_month_by_name
92
+
93
+ private :new
94
+ end
95
+
96
+ end
97
+ end
98
+ end