hummingbird 1.0.0 → 1.0.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/.gitignore CHANGED
@@ -12,5 +12,9 @@ Gemfile.lock
12
12
  # SimpleCov
13
13
  /coverage/
14
14
 
15
+ # Yard
16
+ /.yardoc/
17
+ /doc/
18
+
15
19
  # OS X
16
20
  .DS_Store
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'yard'
3
4
 
4
5
  Rake::TestTask.new do |t|
5
6
  t.libs << 'test'
@@ -7,4 +8,6 @@ Rake::TestTask.new do |t|
7
8
  t.verbose = true
8
9
  end
9
10
 
11
+ YARD::Rake::YardocTask.new
12
+
10
13
  task :default => :test
data/hummingbird.gemspec CHANGED
@@ -22,10 +22,12 @@ DESC
22
22
  gem.add_dependency 'sequel'
23
23
  gem.add_dependency 'optimism'
24
24
 
25
- gem.add_development_dependency 'rake'
26
- gem.add_development_dependency 'minitest'
27
25
  gem.add_development_dependency 'guard'
28
26
  gem.add_development_dependency 'guard-minitest'
27
+ gem.add_development_dependency 'minitest'
28
+ gem.add_development_dependency 'rake'
29
+ gem.add_development_dependency 'redcarpet'
29
30
  gem.add_development_dependency 'simplecov'
30
31
  gem.add_development_dependency 'sqlite3'
32
+ gem.add_development_dependency 'yard'
31
33
  end
data/lib/hummingbird.rb CHANGED
@@ -3,5 +3,7 @@ require 'hummingbird/configuration'
3
3
  require 'hummingbird/plan'
4
4
  require 'hummingbird/database'
5
5
 
6
- class Hummingbird
6
+ # Empty module to facilitate loading {Hummingbird::Configuration},
7
+ # {Hummingbird::Database}, and {Hummingbird::Plan}.
8
+ module Hummingbird
7
9
  end
@@ -1,10 +1,54 @@
1
1
  require 'optimism'
2
2
 
3
- class Hummingbird
3
+ module Hummingbird
4
+ # Helper class to handle reading configuration options from YAML
5
+ # files.
4
6
  class Configuration
7
+ # The name of the default configuration file.
5
8
  CONFIG_FILE = 'hummingbird.yml'
9
+ # The name of the default user specific configuration file. Used
10
+ # for overriding settings in the default configuration file, or
11
+ # providing values for settings missing from there.
6
12
  USER_CONFIG_FILE = '.hummingbird.yml'
7
13
 
14
+ # Provides access to the settings found in the configuration file,
15
+ # and user specific configuration file. By default it will look
16
+ # for {CONFIG_FILE}, and {USER_CONFIG_FILE} in the specified
17
+ # `config_dir`. These can be overridden.
18
+ #
19
+ # The YAML configuration files should be in the format:
20
+ #
21
+ # ---
22
+ # basedir: 'sql'
23
+ # planfile: 'application.plan'
24
+ # migrations_dir: 'migrations-dir'
25
+ # migrations_table: 'application_migrations'
26
+ # connection_string: 'sequel connection string'
27
+ #
28
+ # See the individual access methods for details about each
29
+ # setting.
30
+ #
31
+ # @see #basedir
32
+ # @see #planfile
33
+ # @see #migrations_dir
34
+ # @see #migrations_table
35
+ # @see #connection_string
36
+ #
37
+ # @param [String] config_dir The directory in which to look for
38
+ # configuration files.
39
+ #
40
+ # @param [Hash] opts Overrides for the default configuration file
41
+ # names and locations.
42
+ #
43
+ # @option opts [String] :config_file Override the default
44
+ # configuration file name and location. This can be either a
45
+ # path relative to the `config_dir`, or an absolute path to the
46
+ # configuration file.
47
+ #
48
+ # @option opts [String] :user_config_file Override the default
49
+ # user specific configuration file name and location. This can
50
+ # be either a path relative to the `config_dir`, or an absolute
51
+ # path to the configuration file.
8
52
  def initialize(config_dir,opts={})
9
53
  opts[:config_file] ||= CONFIG_FILE
10
54
  opts[:user_config_file] ||= USER_CONFIG_FILE
@@ -17,22 +61,55 @@ class Hummingbird
17
61
  @config = Optimism.require(*config_file_names)
18
62
  end
19
63
 
64
+ # The directory on which to base relative paths of other settings.
65
+ # This directory itself is relative to `Dir.getwd` unless
66
+ # specified as an absolute path. This defaults to '.' (the
67
+ # current working directory).
68
+ #
69
+ # @return [String] The absolute path to the directory specified in
70
+ # the config file.
20
71
  def basedir
21
72
  @basedir ||= File.expand_path(@config[:basedir] || '.', @config_dir)
22
73
  end
23
74
 
75
+ # The file containing the list of migrations to be run in the
76
+ # order that they should be run. This is relative to {#basedir}
77
+ # when specified in the configuration file, unless specified as an
78
+ # absolute path. Defaults to `hummingbird.plan`.
79
+ #
80
+ # @see #basedir
81
+ #
82
+ # @return [String] The absolute path to the plan file.
24
83
  def planfile
25
84
  @planfile ||= File.expand_path(@config[:planfile] || 'hummingbird.plan', basedir)
26
85
  end
27
86
 
87
+ # The base directory for all migration files. This is relative to
88
+ # {#basedir} when specified in the configuration file, unless
89
+ # specified as an absolute path. Defaults to `migrations`.
90
+ #
91
+ # @see #basedir
92
+ #
93
+ # @return [String] The absolute path to the plan file.
28
94
  def migrations_dir
29
95
  @migrations_dir ||= File.expand_path(@config[:migrations_dir] || 'migrations', basedir)
30
96
  end
31
97
 
98
+ # The name of the migrations table used to keep track of which
99
+ # migrations have been successfully run, and when they were run.
100
+ # Defaults to `hummingbird_migrations`.
101
+ #
102
+ # @return [Symbol] The name of the migrations table as a symbol.
32
103
  def migrations_table
33
104
  @migrations_table ||= (@config[:migrations_table] || :hummingbird_migrations).to_sym
34
105
  end
35
106
 
107
+ # The {http://sequel.rubyforge.org/ Sequel} compatible connection
108
+ # string. This has no default, and must be specified in the
109
+ # configuration file, or provided by another means.
110
+ #
111
+ # @return [String] Connection string to be passed to
112
+ # {http://sequel.rubyforge.org/ Sequel}.
36
113
  def connection_string
37
114
  @connection_string ||= @config[:connection_string]
38
115
  end
@@ -1,21 +1,59 @@
1
1
  require 'sequel'
2
2
 
3
- class Hummingbird
3
+ module Hummingbird
4
+ # Class to handle retrieving recorded migrations, as well as running
5
+ # new migrations and recording that they have been run.
4
6
  class Database
7
+ # @param [String] connection_string A
8
+ # {http://sequel.rubyforge.org/ Sequel} compatible connection string.
9
+ #
10
+ # @param [Symbol] migrations_table The name of the table used to
11
+ # keep track of migrations.
5
12
  def initialize(connection_string, migrations_table)
6
13
  @sequel_db = Sequel.connect(connection_string)
7
14
  @migrations_table_name = migrations_table
8
15
  @prepared_run_migration_insert = nil
9
16
  end
10
17
 
18
+ # @return [true, false] Whether or not the migrations table is
19
+ # present in the database.
11
20
  def initialized?
12
21
  @sequel_db.tables.include?(@migrations_table_name)
13
22
  end
14
23
 
24
+ # If the database has yet to be initialized with the migrations
25
+ # table, or if the migrations table has no recorded migrations,
26
+ # this will return an empty array.
27
+ #
28
+ # If there are recorded migrations, this will return an array
29
+ # of hashes. Where the hashes have the following format:
30
+ #
31
+ # {
32
+ # :migration_name => 'name/of/migration.sql',
33
+ # :run_on => 1350683387
34
+ # }
35
+ #
36
+ # `:migration_name` is the path of the migration file relative to
37
+ # the migrations directory. `:run_on` is the time the migration
38
+ # was run, as a unix epoch.
39
+ #
40
+ # @return [Array<Hash{Symbol => String, Number}>] The list of
41
+ # migrations that have already been run, along with when they
42
+ # were run as a unix epoch.
15
43
  def already_run_migrations
16
44
  initialized? ? @sequel_db[@migrations_table_name].order(:run_on).to_a : []
17
45
  end
18
46
 
47
+ # Run the provided SQL in a transaction (provided the DB in
48
+ # question supports transactions). If the SQL successfully runs,
49
+ # then also record the migration in the migration table. The time
50
+ # recorded for the `run_on` of the migration is when the migration
51
+ # _finished_, not when it _started_.
52
+ #
53
+ # @param [String] name The name of the migration to run (as listed
54
+ # in the .plan file).
55
+ #
56
+ # @param [String] sql The SQL to run.
19
57
  def run_migration(name,sql)
20
58
  @prepared_run_migration_insert ||= @sequel_db[@migrations_table_name].prepare(:insert, :record_migration, migration_name: :$name, run_on: :$date)
21
59
 
@@ -2,27 +2,75 @@ require 'hummingbird/plan_error'
2
2
 
3
3
  require 'pathname'
4
4
 
5
- class Hummingbird
5
+ module Hummingbird
6
+ # This is responsible for parsing the `.plan` file, and for
7
+ # verifying it against the migrations stored on disk.
6
8
  class Plan
7
- attr_reader :migration_dir, :planned_files
8
-
9
+ # @return [String] The base directory for all of the migration
10
+ # files referenced in the `.plan` file.
11
+ attr_reader :migration_dir
12
+
13
+ # This list has not been verified against the files on disk, or
14
+ # against the database in any way.
15
+ #
16
+ # @return [Array<String>] The list of all files referenced in the
17
+ # `.plan` file.
18
+ attr_reader :planned_files
19
+
20
+ # @param [String] planfile The path to the `.plan` file.
21
+ #
22
+ # @param [String] migration_dir The path to the base directory for
23
+ # all of the migration files referenced in the `.plan` file.
9
24
  def initialize(planfile, migration_dir)
10
25
  @planned_files = parse_plan(planfile)
11
26
  @migration_dir = migration_dir
12
27
  end
13
28
 
29
+ # @return [Array<String>] All files found under {#migration_dir}.
14
30
  def migration_files
15
31
  @migration_files ||= get_migration_files
16
32
  end
17
33
 
34
+ # @return [Array<String>] All {#migration_files} that are not in
35
+ # {#planned_files}.
18
36
  def files_missing_from_plan
19
37
  migration_files - planned_files
20
38
  end
21
39
 
40
+ # If this list is not empty, there is probably an error as there
41
+ # are files that have been planned to run that do not exist on
42
+ # disk.
43
+ #
44
+ # @return [Array<String>] All {#planned_files} that are not in
45
+ # {#migration_files}.
22
46
  def files_missing_from_migration_dir
23
47
  planned_files - migration_files
24
48
  end
25
49
 
50
+ # The names, and SQL for migrations that have yet to be run
51
+ # according to the list of already run migrations in
52
+ # `already_run_migrations`.
53
+ #
54
+ # This delegates to {#to_be_run_migration_file_names} and attaches
55
+ # the associated SQL to each migration.
56
+ #
57
+ # @see #to_be_run_migration_file_names
58
+ # @see Hummingbird::Database#already_run_migrations
59
+ #
60
+ # @param [Array<Hash{Symbol => String}>] already_run_migrations
61
+ # This takes the same format array as output by
62
+ # {Hummingbird::Database#already_run_migrations}.
63
+ #
64
+ # @return [Array<Hash{Symbol => String}>] This is the list of
65
+ # migrations to be run, with their associated SQL as
66
+ # `[{ :migration_name => String, :sql => String }, ...]`.
67
+ #
68
+ # @raise [Hummingbird::PlanError] If any of the
69
+ # already_run_migrations are not in the list of planned files.
70
+ #
71
+ # @raise [Hummingbird::PlanError] If any of the
72
+ # if any of the files in already_run_migrations appear out of
73
+ # order relative to the planned files.
26
74
  def migrations_to_be_run(already_run_migrations)
27
75
  to_be_run_migration_file_names(already_run_migrations).map do |f|
28
76
  {
@@ -32,6 +80,25 @@ class Hummingbird
32
80
  end
33
81
  end
34
82
 
83
+ # It compares `already_run_migrations` against the list of planned
84
+ # migrations, and return the list of migrations that have yet to
85
+ # be run.
86
+ #
87
+ # @see Hummingbird::Database#already_run_migrations
88
+ #
89
+ # @param [Array<Hash{Symbol => String}>] already_run_migrations
90
+ # This takes the same format array as output by
91
+ # {Hummingbird::Database#already_run_migrations}.
92
+ #
93
+ # @return [Array<String>] The list of migration names that have
94
+ # not yet been run.
95
+ #
96
+ # @raise [Hummingbird::PlanError] If any of the
97
+ # already_run_migrations are not in the list of planned files.
98
+ #
99
+ # @raise [Hummingbird::PlanError] If any of the
100
+ # if any of the files in already_run_migrations appear out of
101
+ # order relative to the planned files.
35
102
  def to_be_run_migration_file_names(already_run_migrations)
36
103
  return planned_files if already_run_migrations.empty?
37
104
 
@@ -53,6 +120,12 @@ class Hummingbird
53
120
  files
54
121
  end
55
122
 
123
+ # Return the contents of the specified migration file.
124
+ #
125
+ # @param [String] migration_file The path to the desired migration
126
+ # file, relative to {#migration_dir}.
127
+ #
128
+ # @return [String] The contents of the specified migration file.
56
129
  def get_migration_contents(migration_file)
57
130
  File.read(File.expand_path(migration_file, @migration_dir))
58
131
  end
@@ -1,7 +1,34 @@
1
- class Hummingbird
1
+ module Hummingbird
2
+ # Exception class with extra information available to examine what
3
+ # caused a validation error comparing the planned migrations against
4
+ # the recorded migrations.
2
5
  class PlanError < Exception
3
- attr_reader :already_run_migrations, :planned_files
6
+ # @see Hummingbird::Database#already_run_migrations
7
+ #
8
+ # @return [Array<Hash{Symbol => String}>] The
9
+ # {Hummingbird::Database#already_run_migrations} at the time of
10
+ # the plan error.
11
+ attr_reader :already_run_migrations
4
12
 
13
+ # @see Hummingbird::Plan#planned_files
14
+ #
15
+ # @return [Array<String>] The {Hummingbird::Plan#planned_files} at
16
+ # the time of the plan error.
17
+ attr_reader :planned_files
18
+
19
+ # @see Hummingbird::Database#already_run_migrations
20
+ # @see Hummingbird::Plan#planned_files
21
+ #
22
+ # @param [String] msg A user friendly explanation of what
23
+ # triggered the PlanError.
24
+ #
25
+ # @param [Array<String>] planned_files The
26
+ # {Hummingbird::Plan#planned_files} at the time of the plan
27
+ # error.
28
+ #
29
+ # @param [Array<Hash{Symbol => String}>] already_run_migrations
30
+ # The {Hummingbird::Database#already_run_migrations} at the time
31
+ # of the plan error.
5
32
  def initialize(msg,planned_files,already_run_migrations)
6
33
  super(msg)
7
34
  @planned_files = planned_files
@@ -1,3 +1,4 @@
1
- class Hummingbird
2
- VERSION = "1.0.0"
1
+ module Hummingbird
2
+ # The version of Hummingbird.
3
+ VERSION = "1.0.1"
3
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hummingbird
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-18 00:00:00.000000000 Z
12
+ date: 2012-10-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sequel
@@ -44,7 +44,23 @@ dependencies:
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
46
  - !ruby/object:Gem::Dependency
47
- name: rake
47
+ name: guard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: guard-minitest
48
64
  requirement: !ruby/object:Gem::Requirement
49
65
  none: false
50
66
  requirements:
@@ -76,7 +92,7 @@ dependencies:
76
92
  - !ruby/object:Gem::Version
77
93
  version: '0'
78
94
  - !ruby/object:Gem::Dependency
79
- name: guard
95
+ name: rake
80
96
  requirement: !ruby/object:Gem::Requirement
81
97
  none: false
82
98
  requirements:
@@ -92,7 +108,7 @@ dependencies:
92
108
  - !ruby/object:Gem::Version
93
109
  version: '0'
94
110
  - !ruby/object:Gem::Dependency
95
- name: guard-minitest
111
+ name: redcarpet
96
112
  requirement: !ruby/object:Gem::Requirement
97
113
  none: false
98
114
  requirements:
@@ -139,6 +155,22 @@ dependencies:
139
155
  - - ! '>='
140
156
  - !ruby/object:Gem::Version
141
157
  version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: yard
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
142
174
  description: Write your DB migrations in SQL and run them, hold the magic.
143
175
  email:
144
176
  - jacob@technosorcery.net
@@ -205,7 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
237
  version: '0'
206
238
  segments:
207
239
  - 0
208
- hash: -2384325587943083337
240
+ hash: -2188429721350549035
209
241
  requirements: []
210
242
  rubyforge_project:
211
243
  rubygems_version: 1.8.24
@@ -237,3 +269,4 @@ test_files:
237
269
  - test/lib/hummingbird/version_test.rb
238
270
  - test/test_helper.rb
239
271
  - test/test_helper_test.rb
272
+ has_rdoc: