migrant 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.2.0
data/lib/datatype/base.rb CHANGED
@@ -3,7 +3,7 @@ module DataType
3
3
 
4
4
  class Base
5
5
  attr_accessor :aliases
6
-
6
+
7
7
  # Pass the developer's ActiveRecord::Base structure and we'll
8
8
  # decide the best structure
9
9
  def initialize(options={})
@@ -12,25 +12,25 @@ module DataType
12
12
  @field = options.delete(:field)
13
13
  @aliases = options.delete(:was) || Array.new
14
14
  end
15
-
15
+
16
16
  # Default is 'ye good ol varchar(255)
17
17
  def column
18
18
  {:type => :string}.merge(@options)
19
19
  end
20
-
20
+
21
21
  def mock
22
22
  @value || self.class.default_mock
23
23
  end
24
-
24
+
25
25
  def self.default_mock
26
26
  "Some string"
27
27
  end
28
-
28
+
29
29
  # Decides if and how a column will be changed
30
30
  # Provide the details of a previously column, or simply nil to create a new column
31
- def structure_changes_from(current_structure = nil)
31
+ def structure_changes_from(current_structure = nil)
32
32
  new_structure = column
33
-
33
+
34
34
  if current_structure
35
35
  # General RDBMS data loss scenarios
36
36
  raise DataType::DangerousMigration if (new_structure[:type] != :text && [:string, :text].include?(current_structure[:type]) && new_structure[:type] != current_structure[:type])
@@ -46,7 +46,7 @@ module DataType
46
46
  column
47
47
  end
48
48
  end
49
-
49
+
50
50
  def self.migrant_data_type?; true; end
51
51
  end
52
52
  end
@@ -58,9 +58,9 @@ require 'datatype/date'
58
58
  require 'datatype/fixnum'
59
59
  require 'datatype/float'
60
60
  require 'datatype/foreign_key'
61
- require 'datatype/hash'
62
61
  require 'datatype/polymorphic'
63
62
  require 'datatype/range'
64
63
  require 'datatype/string'
65
64
  require 'datatype/symbol'
66
65
  require 'datatype/time'
66
+
@@ -4,29 +4,34 @@ module Migrant
4
4
  def structure(&block)
5
5
  # Using instance_*evil* to get the neater DSL on the models.
6
6
  # So, my_field in the structure block actually calls Migrant::Schema.my_field
7
-
7
+
8
8
  if self.superclass == ActiveRecord::Base
9
9
  @schema ||= Schema.new
10
10
  @schema.add_associations(self.reflect_on_all_associations)
11
11
  @schema.define_structure(&block)
12
+ @schema.validations.each do |field, validation|
13
+ validation = (validation.class == Hash)? validation : { validation => true }
14
+ self.validates(field, validation)
15
+ end
12
16
  else
13
17
  self.superclass.structure(&block) # For STI, cascade all fields onto the parent model
14
18
  @schema = InheritedSchema.new(self.superclass.schema)
19
+
15
20
  end
16
21
  end
17
-
22
+
18
23
  # Same as defining a structure block, but with no attributes besides
19
24
  # relationships (such as in a many-to-many)
20
25
  def no_structure
21
26
  structure {}
22
27
  end
23
-
28
+
24
29
  def mock(attributes={}, recursive=true)
25
30
  attribs = @schema.columns.reject { |column| column.is_a?(DataType::ForeignKey)}.collect { |name, data_type| [name, data_type.mock] }.flatten
26
31
 
27
32
  # Only recurse to one level, otherwise things get way too complicated
28
33
  if recursive
29
- attribs += self.reflect_on_all_associations(:belongs_to).collect do |association|
34
+ attribs += self.reflect_on_all_associations(:belongs_to).collect do |association|
30
35
  begin
31
36
  (association.klass.respond_to?(:mock))? [association.name, association.klass.mock({}, false)] : nil
32
37
  rescue NameError; nil; end # User hasn't defined association, just skip it
@@ -36,3 +41,4 @@ module Migrant
36
41
  end
37
42
  end
38
43
  end
44
+
@@ -9,23 +9,19 @@ module Migrant
9
9
  # into a schema on that model class by calling method_missing(my_field)
10
10
  # and deciding what the best schema type is for the user's requiredments
11
11
  class Schema
12
- # Global scope resolution operators will make the Migrant DSL fucking useless
13
- # So, I am doing this rather than inhering from SimpleObject, just live with it.
14
- # instance_methods.each { |m| raise 'fuckoff' if m == 'system' ; undef_method unless ['__send__', 'object_id'].include?(m) }
15
-
16
- attr_accessor :indexes, :columns, :methods_to_alias
12
+ attr_accessor :indexes, :columns, :validations, :methods_to_alias
17
13
 
18
14
  def initialize
19
15
  @proxy = SchemaProxy.new(self)
20
16
  @columns = Hash.new
21
17
  @indexes = Array.new
18
+ @validations = Hash.new
22
19
  @methods_to_alias = Array.new
23
20
  end
24
21
 
25
22
  def define_structure(&block)
26
23
  # Runs method_missing on columns given in the model "structure" DSL
27
24
  @proxy.translate_fancy_dsl(&block) if block_given?
28
- # self.instance_eval(&block) if block_given?
29
25
  end
30
26
 
31
27
  def add_associations(associations)
@@ -58,8 +54,17 @@ module Migrant
58
54
  def add_field(field, data_type = nil, options = {})
59
55
  data_type = DataType::String if data_type.nil?
60
56
 
57
+ # Fields that do special things go here.
58
+ if field == :timestamps
59
+ add_field(:updated_at, :datetime)
60
+ add_field(:created_at, :datetime)
61
+ return true
62
+ end
63
+
61
64
  # Add index if explicitly asked
62
65
  @indexes << field if options.delete(:index) || data_type.class.to_s == 'Hash' && data_type.delete(:index)
66
+ @validations[field] = options.delete(:validates) if options[:validates]
67
+
63
68
  options.merge!(:field => field)
64
69
 
65
70
  # Matches: description DataType::Paragraph, :index => true
@@ -112,7 +117,7 @@ module Migrant
112
117
 
113
118
  def method_missing(*args, &block)
114
119
  field = args.slice!(0)
115
- data_type = args.slice!(0) unless args.first.nil?
120
+ data_type = args.slice!(0) unless args.first.nil? || args.first.respond_to?(:keys)
116
121
 
117
122
  @binding.add_field(field, data_type, args.extract_options!)
118
123
  end
data/migrant.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{migrant}
8
- s.version = "0.1.5"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Pascal Houliston"]
@@ -30,7 +30,6 @@ Gem::Specification.new do |s|
30
30
  "lib/datatype/fixnum.rb",
31
31
  "lib/datatype/float.rb",
32
32
  "lib/datatype/foreign_key.rb",
33
- "lib/datatype/hash.rb",
34
33
  "lib/datatype/polymorphic.rb",
35
34
  "lib/datatype/range.rb",
36
35
  "lib/datatype/string.rb",
@@ -83,12 +82,14 @@ Gem::Specification.new do |s|
83
82
  "test/rails_app/vendor/plugins/.gitkeep",
84
83
  "test/test_data_schema.rb",
85
84
  "test/test_migration_generator.rb",
85
+ "test/test_validations.rb",
86
86
  "test/verified_output/migrations/business_id.rb",
87
87
  "test/verified_output/migrations/create_business_categories.rb",
88
88
  "test/verified_output/migrations/create_businesses.rb",
89
89
  "test/verified_output/migrations/create_categories.rb",
90
90
  "test/verified_output/migrations/create_reviews.rb",
91
91
  "test/verified_output/migrations/create_users.rb",
92
+ "test/verified_output/migrations/created_at.rb",
92
93
  "test/verified_output/migrations/estimated_value_notes.rb",
93
94
  "test/verified_output/migrations/landline.rb"
94
95
  ]
@@ -123,12 +124,14 @@ Gem::Specification.new do |s|
123
124
  "test/rails_app/test/test_helper.rb",
124
125
  "test/test_data_schema.rb",
125
126
  "test/test_migration_generator.rb",
127
+ "test/test_validations.rb",
126
128
  "test/verified_output/migrations/business_id.rb",
127
129
  "test/verified_output/migrations/create_business_categories.rb",
128
130
  "test/verified_output/migrations/create_businesses.rb",
129
131
  "test/verified_output/migrations/create_categories.rb",
130
132
  "test/verified_output/migrations/create_reviews.rb",
131
133
  "test/verified_output/migrations/create_users.rb",
134
+ "test/verified_output/migrations/created_at.rb",
132
135
  "test/verified_output/migrations/estimated_value_notes.rb",
133
136
  "test/verified_output/migrations/landline.rb"
134
137
  ]
@@ -4,7 +4,7 @@ require 'rake'
4
4
  def rake_migrate
5
5
  Dir.chdir(Rails.root.join('.')) do
6
6
  Rake::Task['db:migrate'].execute
7
- end
7
+ end
8
8
  end
9
9
 
10
10
 
@@ -15,13 +15,13 @@ class TestMigrationGenerator < Test::Unit::TestCase
15
15
  if migration_file.include?(template)
16
16
  correct = File.join(File.dirname(__FILE__), 'verified_output', 'migrations', template+'.rb')
17
17
  assert_equal(File.open(correct, 'r') { |r| r.read}.strip,
18
- File.open(migration_file, 'r') { |r| r.read}.strip,
18
+ File.open(migration_file, 'r') { |r| r.read}.strip,
19
19
  "Generated migration #{migration_file} does not match template #{correct}")
20
20
  rake_migrate
21
21
  return
22
22
  end
23
23
  end
24
- flunk "No migration could be found"
24
+ flunk "No migration could be found"
25
25
  end
26
26
 
27
27
  context "The migration generator" do
@@ -35,7 +35,7 @@ class TestMigrationGenerator < Test::Unit::TestCase
35
35
  end
36
36
  rake_migrate
37
37
  end
38
-
38
+
39
39
  should "generate a migration for new added fields" do
40
40
  Business.structure do
41
41
  estimated_value 5000.0
@@ -43,41 +43,48 @@ class TestMigrationGenerator < Test::Unit::TestCase
43
43
  end
44
44
  run_against_template('estimated_value_notes')
45
45
  end
46
-
46
+
47
47
  should "generate a migration to alter existing columns where no data loss would occur" do
48
48
  Business.structure do
49
49
  landline :text
50
50
  end
51
-
51
+
52
52
  run_against_template('landline')
53
53
  end
54
-
54
+
55
55
  should "generate a migration to alter existing columns while adding a new table" do
56
56
  load File.join(File.dirname(__FILE__), 'additional_models', 'review.rb')
57
57
  User.belongs_to(:business) # To generate a business_id on a User
58
58
  User.no_structure # To force schema update
59
-
59
+
60
60
  run_against_template('create_reviews')
61
61
  run_against_template('business_id')
62
62
  end
63
-
63
+
64
+ should "generate created_at and updated_at when given the column timestamps" do
65
+ Business.structure do
66
+ timestamps
67
+ end
68
+ run_against_template('created_at')
69
+ end
70
+
64
71
  should "not change existing columns where data loss may occur" do
65
72
  Business.structure do
66
- landline :integer # Was previously a string, which obviously may incur data loss
73
+ landline :integer # Was previously a string, which obviously may incur data loss
67
74
  end
68
- assert_equal(false, Migrant::MigrationGenerator.new.run, "MigrationGenerator ran a dangerous migration!")
75
+ assert_equal(false, Migrant::MigrationGenerator.new.run, "MigrationGenerator ran a dangerous migration!")
69
76
  Business.structure do
70
77
  landline :text # Undo our bad for the next tests
71
- end
78
+ end
72
79
  end
73
-
80
+
74
81
  should "exit immediately if there are pending migrations" do
75
82
  manual_migration = Rails.root.join("db/migrate/9999999999999999_my_new_migration.rb")
76
83
  File.open(manual_migration, 'w') { |f| f.write ' ' }
77
84
  assert_equal(false, Migrant::MigrationGenerator.new.run)
78
85
  File.delete(manual_migration)
79
86
  end
80
-
87
+
81
88
  should "still create sequential migrations for the folks not using timestamps" do
82
89
  Business.structure do
83
90
  new_field_i_made_up
@@ -85,8 +92,8 @@ class TestMigrationGenerator < Test::Unit::TestCase
85
92
  # Remove migrations
86
93
  ActiveRecord::Base.timestamped_migrations = false
87
94
  assert_equal true, Migrant::MigrationGenerator.new.run, "Migration Generator reported an error"
88
- ActiveRecord::Base.timestamped_migrations = true
89
-
95
+ ActiveRecord::Base.timestamped_migrations = true
96
+
90
97
  assert_equal(Dir.glob(File.join(File.dirname(__FILE__), 'rails_app', 'db' ,'migrate', '*.rb')).select { |migration_file| migration_file.include?('new_field_i_made_up') }.length,
91
98
  1,
92
99
  "Migration should have been generated (without a duplicate)")
@@ -98,7 +105,7 @@ class TestMigrationGenerator < Test::Unit::TestCase
98
105
  test_date_mockup DataType::Date
99
106
  test_float_mockup DataType::Float
100
107
  test_range_mockup DataType::Range
101
-
108
+
102
109
  end
103
110
  BusinessCategory.belongs_to(:nonexistant_class, :polymorphic => true)
104
111
  assert_equal true, Migrant::MigrationGenerator.new.run, "Migration Generator reported an error"
@@ -111,4 +118,4 @@ class TestMigrationGenerator < Test::Unit::TestCase
111
118
 
112
119
  end
113
120
  end
114
-
121
+
@@ -0,0 +1,34 @@
1
+ require 'helper'
2
+
3
+ class TestValidations < Test::Unit::TestCase
4
+ should "validate via ActiveRecord when the validates symbol is supplied" do
5
+ Business.structure do
6
+ website :string, :validates => :presence
7
+ end
8
+
9
+ business = Business.create
10
+ assert(business.errors.include?(:website), "Validation was not applied")
11
+ end
12
+
13
+ should "validate via ActiveRecord when the full validation hash is supplied" do
14
+ Category.structure do
15
+ summary :string, :validates => { :format => { :with => /Symphony\d/ } }
16
+ end
17
+
18
+ bad_category = Category.create
19
+ good_category = Category.create(:summary => "Symphony5")
20
+ assert(bad_category.errors.include?(:summary), "Validation was not applied")
21
+ assert(!good_category.errors.include?(:summary), "Validation options were incorrect")
22
+ end
23
+
24
+ should "validate via ActiveRecord when no field name is given" do
25
+ User.structure do
26
+ email :validates => :presence
27
+ end
28
+
29
+ user = User.create
30
+ assert(user.errors.include?(:email), "Validation was not applied")
31
+ end
32
+
33
+ end
34
+
@@ -0,0 +1,11 @@
1
+ class BusinessesModifyFieldsUpdatedAtCreatedAt < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :businesses, :updated_at, :datetime
4
+ add_column :businesses, :created_at, :datetime
5
+ end
6
+
7
+ def self.down
8
+ remove_column :businesses, :updated_at
9
+ remove_column :businesses, :created_at
10
+ end
11
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
8
- - 5
9
- version: 0.1.5
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Pascal Houliston
@@ -163,7 +163,6 @@ files:
163
163
  - lib/datatype/fixnum.rb
164
164
  - lib/datatype/float.rb
165
165
  - lib/datatype/foreign_key.rb
166
- - lib/datatype/hash.rb
167
166
  - lib/datatype/polymorphic.rb
168
167
  - lib/datatype/range.rb
169
168
  - lib/datatype/string.rb
@@ -216,12 +215,14 @@ files:
216
215
  - test/rails_app/vendor/plugins/.gitkeep
217
216
  - test/test_data_schema.rb
218
217
  - test/test_migration_generator.rb
218
+ - test/test_validations.rb
219
219
  - test/verified_output/migrations/business_id.rb
220
220
  - test/verified_output/migrations/create_business_categories.rb
221
221
  - test/verified_output/migrations/create_businesses.rb
222
222
  - test/verified_output/migrations/create_categories.rb
223
223
  - test/verified_output/migrations/create_reviews.rb
224
224
  - test/verified_output/migrations/create_users.rb
225
+ - test/verified_output/migrations/created_at.rb
225
226
  - test/verified_output/migrations/estimated_value_notes.rb
226
227
  - test/verified_output/migrations/landline.rb
227
228
  has_rdoc: true
@@ -238,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
239
  requirements:
239
240
  - - ">="
240
241
  - !ruby/object:Gem::Version
241
- hash: -454091891
242
+ hash: -984502067
242
243
  segments:
243
244
  - 0
244
245
  version: "0"
@@ -284,11 +285,13 @@ test_files:
284
285
  - test/rails_app/test/test_helper.rb
285
286
  - test/test_data_schema.rb
286
287
  - test/test_migration_generator.rb
288
+ - test/test_validations.rb
287
289
  - test/verified_output/migrations/business_id.rb
288
290
  - test/verified_output/migrations/create_business_categories.rb
289
291
  - test/verified_output/migrations/create_businesses.rb
290
292
  - test/verified_output/migrations/create_categories.rb
291
293
  - test/verified_output/migrations/create_reviews.rb
292
294
  - test/verified_output/migrations/create_users.rb
295
+ - test/verified_output/migrations/created_at.rb
293
296
  - test/verified_output/migrations/estimated_value_notes.rb
294
297
  - test/verified_output/migrations/landline.rb
data/lib/datatype/hash.rb DELETED
@@ -1,10 +0,0 @@
1
- module DataType
2
- class Hash < Base
3
- def column
4
- @options = @value # Assign developer's options verbatim
5
- super
6
- end
7
- end
8
- end
9
-
10
-