mini_record-compat 0.2.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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - rbx-2.0
6
+ - ruby-head
7
+ - ree
8
+ notifications:
9
+ recipients:
10
+ - d.dagostino@lipsiasoft.com
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mini_record.gemspec
4
+ gem "rake"
5
+ gem "minitest"
6
+ gem "sqlite3"
7
+ gem 'mysql'
8
+ gem 'sqlite3-ruby'
9
+ gem 'pg'
10
+
11
+ gemspec
data/README.md ADDED
@@ -0,0 +1,167 @@
1
+ [![Build Status](https://secure.travis-ci.org/DAddYE/mini_record.png)](http://travis-ci.org/DAddYE/mini_record)
2
+
3
+
4
+ MiniRecord is a micro extension for our `ActiveRecord` gem.
5
+ With MiniRecord you can add the ability to create columns outside the default `schema.rb`, directly
6
+ in your **model** in a similar way that should know in others projects
7
+ like DataMapper, MongoMapper or MongoID.
8
+
9
+ My inspiration come from this handy [project](https://github.com/pjhyett/auto_migrations).
10
+
11
+ ## Features
12
+
13
+ * Define columns/properties inside your model
14
+ * Perform migrations automatically
15
+ * Auto upgrade your schema, so if you know what you are doing you don't lost your existing data!
16
+ * Add, Remove, Change Columns; Add, Remove, Change indexes
17
+
18
+ ## Instructions
19
+
20
+ What you need is to move/remove your `db/schema.rb`.
21
+ This avoid conflicts.
22
+
23
+ Add to your `Gemfile`:
24
+
25
+ ``` rb
26
+ gem 'mini_record'
27
+ ```
28
+
29
+ That's all!
30
+
31
+ ## Examples
32
+
33
+ Remember that inside properties you can use all migrations methods,
34
+ see [documentation](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html)
35
+
36
+ ``` rb
37
+ class Post < ActiveRecord::Base
38
+ col :title_en, :title_jp
39
+ col :description_en, :description_jp, :as => :text
40
+ col :permalink, :index => true, :limit => 50
41
+ col :comments_count, :as => :integer
42
+ col :category, :as => :references, :index => true
43
+ end
44
+ Post.auto_upgrade!
45
+ ```
46
+
47
+ If you don't like `col` there are also few aliases: `key, field, property, attribute`
48
+
49
+ Instead of `:as => :my_type` you can use `:type => :my_type`
50
+
51
+ Option `:as` or `:type` if not provided is `:string` by default, you can use all ActiveRecord types:
52
+
53
+ ``` rb
54
+ :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean
55
+ :references, :belongs_to, :polymorphic, :timestamp
56
+ ```
57
+
58
+ You can provide others ActiveRecord options like:
59
+
60
+ ``` rb
61
+ :limit, :default, :null, :precision, :scale
62
+
63
+ # example
64
+ class Foo < ActiveRecord::Base
65
+ col :title, :default => "MyTitle" # :as => :string is by default
66
+ col :price, :as => :decimal, :scale => 8, :precision => 2
67
+ end
68
+ ```
69
+
70
+ See [ActiveRecord::TableDefinition](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html)
71
+ for more details.
72
+
73
+ Finally, when you execute `MyModel.auto_upgrade!`, missing columns, indexes and tables will be created on the fly.
74
+ Indexes and columns present in the db but **not** in your model schema will be **deleted*** also in your db.
75
+
76
+ ### Single Table Inheritance
77
+
78
+ MiniRecord as ActiveRecord support STI plus some goodness, see our specs for more details.
79
+
80
+ ### Adding a new column
81
+
82
+ Super easy, open your model and just add it:
83
+
84
+ ``` rb
85
+ class Post < ActiveRecord::Base
86
+ col :title
87
+ col :body, :as => :text # <<- this
88
+ col :permalink, :index => true
89
+ col :comments_count, :as => :integer
90
+ col :category, :as => :references, :index => true
91
+ end
92
+ Post.auto_upgrade!
93
+ ```
94
+
95
+ So now when you invoke `MyModel.auto_upgrade!` you should see a SQL query like `ALTER TABLE` that mean that your existing
96
+ records are happy and safe.
97
+
98
+ ### Removing a column
99
+
100
+ It's exactly the same, but the column will be _really_ deleted without affect other columns.
101
+
102
+ ### Change columns
103
+
104
+ It's not possible for us know when/what column you have renamed, but we can know if you changed the `type` so
105
+ if you change `t.string :name` to `t.text :name` we are be able to perform an `ALTER TABLE`
106
+
107
+ ### Add/Remove indexes
108
+
109
+ In the same ways we manage columns MiniRecord will detect new indexes and indexes that needs to be removed.
110
+ So when you perform `MyModel.auto_upgrade!` a SQL command like:
111
+
112
+ ``` SQL
113
+ PRAGMA index_info('index_people_on_name')
114
+ CREATE INDEX "index_people_on_surname" ON "people" ("surname")
115
+ ```
116
+
117
+ Note that writing it in DSL way you have same options as `add_index` so you are be able to write:
118
+
119
+ ``` rb
120
+ class Fox < ActiveRecord::Base
121
+ col :foo, :index => true
122
+ col :foo, :index => :custom_name
123
+ col :foo, :index => [:foo, :bar]
124
+ col :foo, :index => { :column => [:branch_id, :party_id], :unique => true, :name => 'by_branch_party' }
125
+ end
126
+ ```
127
+
128
+ That is the same of:
129
+
130
+ ``` rb
131
+ class Fox < ActiveRecord::Base
132
+ col :foo
133
+ add_index :foo
134
+ add_index :custom_name
135
+ add_index [:foo, :bar]
136
+ add_index [:branch_id, :party_id], :unique => true, :name => 'by_branch_party'
137
+ end
138
+ ```
139
+
140
+ ## Warnings
141
+
142
+ This software is not super well tested in a production project.
143
+ Im stated to using it in two customer's projects without any problem.
144
+
145
+ ## TODO
146
+
147
+ * reduce the number of methods we add to ActiveRecord::Base objects
148
+
149
+ ## Author
150
+
151
+ DAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye) or take a look at my site [daddye.it](http://www.daddye.it)
152
+
153
+ ## Copyright
154
+
155
+ Copyright (C) 2011 Davide D'Agostino - [@daddye](http://twitter.com/daddye)
156
+
157
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
158
+ associated documentation files (the “Software”), to deal in the Software without restriction, including without
159
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
160
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
161
+
162
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
163
+
164
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
165
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
166
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
167
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake"
3
+ require "rake/testtask"
4
+
5
+ %w(install release).each do |task|
6
+ Rake::Task[task].enhance do
7
+ sh "rm -rf pkg"
8
+ end
9
+ end
10
+
11
+ desc "Bump version on github"
12
+ task :bump do
13
+ if `git status -s`.strip == ""
14
+ puts "\e[31mNothing to commit (working directory clean)\e[0m"
15
+ else
16
+ version = Bundler.load_gemspec(Dir[File.expand_path('../*.gemspec', __FILE__)].first).version
17
+ sh "git add .; git commit -a -m \"Bump to version #{version}\""
18
+ end
19
+ end
20
+
21
+ task :release => :bump
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'spec'
24
+ test.test_files = Dir['spec/**/*_spec.rb']
25
+ test.verbose = true
26
+ end
27
+
28
+ task :test_each_db_adapter do
29
+ %w{ mysql sqlite3 postgresql }.each do |db_adapter|
30
+ puts
31
+ puts "#{'*'*10} Running #{db_adapter} tests"
32
+ puts
33
+ puts `bundle exec rake test TEST=spec/#{db_adapter}_spec.rb`
34
+ end
35
+ end
36
+
37
+ task :default => :test_each_db_adapter
38
+ task :spec => :test_each_db_adapter
@@ -0,0 +1,220 @@
1
+ require 'zlib'
2
+ module MiniRecord
3
+ module AutoSchema
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def table_definition
11
+ return superclass.table_definition unless superclass == ActiveRecord::Base
12
+
13
+ @_table_definition ||= begin
14
+ ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
15
+ end
16
+ end
17
+
18
+ def indexes
19
+ return superclass.indexes unless superclass == ActiveRecord::Base
20
+
21
+ @_indexes ||= {}
22
+ end
23
+
24
+ def col(*args)
25
+ return unless connection?
26
+
27
+ options = args.extract_options!
28
+ type = options.delete(:as) || options.delete(:type) || :string
29
+ args.each do |column_name|
30
+ table_definition.send(type, column_name, options)
31
+ column_name = table_definition.columns[-1].name
32
+ case index_name = options.delete(:index)
33
+ when Hash
34
+ add_index(options.delete(:column) || column_name, index_name)
35
+ when TrueClass
36
+ add_index(column_name)
37
+ when String, Symbol, Array
38
+ add_index(index_name)
39
+ end
40
+ end
41
+ end
42
+ alias :key :col
43
+ alias :property :col
44
+ alias :field :col
45
+ alias :attribute :col
46
+
47
+ def reset_table_definition!
48
+ @_table_definition = nil
49
+ end
50
+ alias :reset_schema! :reset_table_definition!
51
+
52
+ def schema
53
+ reset_table_definition!
54
+ yield table_definition
55
+ table_definition
56
+ end
57
+ alias :keys :schema
58
+ alias :properties :schema
59
+ alias :fields :schema
60
+ alias :attributes :schema
61
+
62
+ def add_index(column_name, options={})
63
+ index_name = shorten_index_name connection.index_name(table_name, :column => column_name)
64
+ indexes[index_name] = options.merge(:column => column_name, :name => index_name)
65
+ index_name
66
+ end
67
+ alias :index :add_index
68
+
69
+ def connection?
70
+ !!connection
71
+ rescue Exception => e
72
+ puts "\e[31m%s\e[0m" % e.message.strip
73
+ false
74
+ end
75
+
76
+ def shorten_index_name(name)
77
+ if name.length < connection.index_name_length
78
+ name
79
+ else
80
+ name[0..(connection.index_name_length-11)] + ::Zlib.crc32(name).to_s
81
+ end
82
+ end
83
+
84
+ def sqlite?
85
+ connection.adapter_name =~ /sqlite/i
86
+ end
87
+
88
+ def mysql?
89
+ connection.adapter_name =~ /mysql/i
90
+ end
91
+
92
+ def postgresql?
93
+ connection.adapter_name =~ /postgresql/i
94
+ end
95
+
96
+ def auto_upgrade!(create_table_options = '')
97
+ return unless connection?
98
+
99
+ # normally activerecord's mysql adapter does this
100
+ if mysql?
101
+ create_table_options ||= 'ENGINE=InnoDB'
102
+ end
103
+
104
+ non_standard_primary_key = if (primary_key_column = table_definition.columns.detect { |column| column.name.to_s == primary_key.to_s })
105
+ primary_key_column.type != :primary_key
106
+ end
107
+
108
+ unless non_standard_primary_key
109
+ table_definition.column :id, :primary_key
110
+ end
111
+
112
+ # Table doesn't exist, create it
113
+ unless connection.table_exists? table_name
114
+
115
+ # avoid using connection.create_table because in 3.0.x it ignores table_definition
116
+ # and it also is too eager about adding a primary key column
117
+ create_sql = "CREATE TABLE "
118
+ create_sql << "#{quoted_table_name} ("
119
+ create_sql << table_definition.to_sql
120
+ create_sql << ") #{create_table_options}"
121
+ connection.execute create_sql
122
+
123
+ if non_standard_primary_key
124
+ if sqlite?
125
+ add_index primary_key, :unique => true
126
+ elsif mysql? or postgresql?
127
+ # can't use add_index method because it won't let you do "PRIMARY KEY"
128
+ connection.execute "ALTER TABLE #{quoted_table_name} ADD PRIMARY KEY (#{quoted_primary_key})"
129
+ else
130
+ raise RuntimeError, "mini_record doesn't support non-standard primary keys for the #{connection.adapter_name} adapter!"
131
+ end
132
+ end
133
+
134
+ reset_column_information
135
+ end
136
+
137
+ # Add to schema inheritance column if necessary
138
+ if descendants.present? && !table_definition.columns.any? { |column| column.name.to_s == inheritance_column.to_s }
139
+ table_definition.column inheritance_column, :string
140
+ end
141
+
142
+ # Grab database columns
143
+ fields_in_db = connection.columns(table_name).inject({}) do |hash, column|
144
+ hash[column.name] = column
145
+ hash
146
+ end
147
+
148
+ # Grab new schema
149
+ fields_in_schema = table_definition.columns.inject({}) do |hash, column|
150
+ hash[column.name.to_s] = column
151
+ hash
152
+ end
153
+
154
+ # Remove fields from db no longer in schema
155
+ (fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
156
+ column = fields_in_db[field]
157
+ connection.remove_column table_name, column.name
158
+ end
159
+
160
+ # Add fields to db new to schema
161
+ (fields_in_schema.keys - fields_in_db.keys).each do |field|
162
+ column = fields_in_schema[field]
163
+ options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
164
+ options[:default] = column.default if !column.default.nil?
165
+ options[:null] = column.null if !column.null.nil?
166
+ connection.add_column table_name, column.name, column.type.to_sym, options
167
+ end
168
+
169
+ # Change attributes of existent columns
170
+ (fields_in_schema.keys & fields_in_db.keys).each do |field|
171
+ if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
172
+ changed = false # flag
173
+ new_type = fields_in_schema[field].type.to_sym
174
+ new_attr = {}
175
+
176
+ # First, check if the field type changed
177
+ if fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym
178
+ changed = true
179
+ end
180
+
181
+ # Special catch for precision/scale, since *both* must be specified together
182
+ # Always include them in the attr struct, but they'll only get applied if changed = true
183
+ new_attr[:precision] = fields_in_schema[field][:precision]
184
+ new_attr[:scale] = fields_in_schema[field][:scale]
185
+
186
+ # Next, iterate through our extended attributes, looking for any differences
187
+ # This catches stuff like :null, :precision, etc
188
+ fields_in_schema[field].each_pair do |att,value|
189
+ next if att == :type or att == :base or att == :name # special cases
190
+ if !value.nil? && value != fields_in_db[field].send(att)
191
+ new_attr[att] = value
192
+ changed = true
193
+ end
194
+ end
195
+
196
+ # Change the column if applicable
197
+ connection.change_column table_name, field, new_type, new_attr if changed
198
+ end
199
+ end
200
+
201
+ # Remove old index
202
+ indexes_in_db = connection.indexes(table_name).map(&:name)
203
+ (indexes_in_db - indexes.keys).each do |name|
204
+ connection.remove_index(table_name, :name => name)
205
+ end
206
+
207
+ # Add indexes
208
+ indexes.each do |name, options|
209
+ options = options.dup
210
+ unless connection.indexes(table_name).detect { |i| i.name == name }
211
+ connection.add_index(table_name, options.delete(:column), options)
212
+ end
213
+ end
214
+
215
+ # Reload column information
216
+ reset_column_information
217
+ end
218
+ end # ClassMethods
219
+ end # AutoSchema
220
+ end # MiniRecord
@@ -0,0 +1,3 @@
1
+ module MiniRecord
2
+ VERSION = "0.2.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'active_record'
3
+ require 'mini_record/auto_schema'
4
+
5
+ ActiveRecord::Base.send(:include, MiniRecord::AutoSchema)
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mini_record/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "mini_record-compat"
7
+ s.version = MiniRecord::VERSION
8
+ s.authors = ["Davide D'Agostino", "Seamus Abshere"]
9
+ s.email = ["d.dagostino@lipsiasoft.com", "seamus@abshere.net"]
10
+ s.homepage = "https://github.com/seamusabshere/mini_record"
11
+ s.summary = %q{Alternate gem published by Seamus Abshere for ActiveRecord 3.0 support. MiniRecord is a micro gem that allow you to write schema inside your model as you can do in DataMapper.}
12
+ s.description = %q{
13
+ With it you can add the ability to create columns outside the default schema, directly
14
+ in your model in a similar way that you just know in others projects
15
+ like DataMapper or MongoMapper.
16
+ }.gsub(/^ {4}/, '')
17
+
18
+ # s.rubyforge_project = "mini_record"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+
25
+ s.add_runtime_dependency "activerecord", ">=3"
26
+
27
+ # dev dependencies appear to be in the Gemfile
28
+ end
data/spec/models.rb ADDED
@@ -0,0 +1,70 @@
1
+ # be sure to set up activerecord before you require this helper
2
+
3
+ class Person < ActiveRecord::Base
4
+ include SpecHelper
5
+ schema do |s|
6
+ s.string :name
7
+ end
8
+ end
9
+
10
+ class Post < ActiveRecord::Base
11
+ include SpecHelper
12
+
13
+ key :title
14
+ key :body
15
+ key :category, :as => :references
16
+ belongs_to :category
17
+ end
18
+
19
+ class Category < ActiveRecord::Base
20
+ include SpecHelper
21
+
22
+ key :title
23
+ has_many :posts
24
+ end
25
+
26
+ class Animal < ActiveRecord::Base
27
+ include SpecHelper
28
+
29
+ key :name, :index => true
30
+ index :id
31
+ end
32
+
33
+ class Pet < ActiveRecord::Base
34
+ include SpecHelper
35
+
36
+ key :name, :index => true
37
+ end
38
+ class Dog < Pet; end
39
+ class Cat < Pet; end
40
+
41
+ class Vegetable < ActiveRecord::Base
42
+ include SpecHelper
43
+
44
+ set_primary_key :latin_name
45
+
46
+ col :latin_name
47
+ col :common_name
48
+ end
49
+
50
+ class User < ActiveRecord::Base
51
+ include SpecHelper
52
+ col :name
53
+ col :surname
54
+ col :role
55
+ set_inheritance_column :role
56
+ end
57
+ class Administrator < User; end
58
+ class Customer < User; end
59
+
60
+ class Fake < ActiveRecord::Base
61
+ include SpecHelper
62
+ col :name, :surname
63
+ col :category, :group, :as => :references
64
+ end
65
+
66
+ class AutomobileMakeModelYearVariant < ActiveRecord::Base
67
+ include SpecHelper
68
+ col :make_model_year_name
69
+ add_index :make_model_year_name
70
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ bin = ENV['TEST_MYSQL_BIN'] || 'mysql'
4
+ username = ENV['TEST_MYSQL_USERNAME'] || 'root'
5
+ password = ENV['TEST_MYSQL_PASSWORD'] || 'password'
6
+ database = ENV['TEST_MYSQL_DATABASE'] || 'test_mini_record'
7
+ cmd = "#{bin} -u #{username} -p#{password}"
8
+
9
+ `#{cmd} -e 'show databases'`
10
+ unless $?.success?
11
+ $stderr.puts "Skipping mysql tests because `#{cmd}` doesn't work"
12
+ exit 0
13
+ end
14
+
15
+ system %{#{cmd} -e "drop database #{database}"}
16
+ system %{#{cmd} -e "create database #{database}"}
17
+
18
+ ActiveRecord::Base.establish_connection(
19
+ 'adapter' => 'mysql',
20
+ 'encoding' => 'utf8',
21
+ 'database' => database,
22
+ 'username' => username,
23
+ 'password' => password
24
+ )
25
+
26
+ # require 'logger'
27
+ # ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new($stdout)
28
+
29
+ require File.expand_path('../models.rb', __FILE__)
30
+
31
+ require File.expand_path('../shared_examples.rb', __FILE__)
@@ -0,0 +1,25 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ createdb_bin = ENV['TEST_CREATEDB_BIN'] || 'createdb'
4
+ dropdb_bin = ENV['TEST_DROPDB_BIN'] || 'dropdb'
5
+ username = ENV['TEST_POSTGRES_USERNAME'] || `whoami`.chomp
6
+ # password = ENV['TEST_POSTGRES_PASSWORD'] || 'password'
7
+ database = ENV['TEST_POSTGRES_DATABASE'] || 'test_mini_record'
8
+
9
+ system %{#{dropdb_bin} #{database}}
10
+ system %{#{createdb_bin} #{database}}
11
+
12
+ ActiveRecord::Base.establish_connection(
13
+ 'adapter' => 'postgresql',
14
+ 'encoding' => 'utf8',
15
+ 'database' => database,
16
+ 'username' => username,
17
+ # 'password' => password
18
+ )
19
+
20
+ # require 'logger'
21
+ # ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new($stdout)
22
+
23
+ require File.expand_path('../models.rb', __FILE__)
24
+
25
+ require File.expand_path('../shared_examples.rb', __FILE__)
@@ -0,0 +1,220 @@
1
+ describe MiniRecord do
2
+ before do
3
+ ActiveRecord::Base.descendants.each do |active_record|
4
+ ActiveRecord::Base.connection.drop_table active_record.table_name if active_record.table_exists?
5
+ end
6
+ end
7
+
8
+ it 'has #schema inside model' do
9
+ # For unknown reason separate specs doesn't works
10
+ ActiveRecord::Base.connection.table_exists?(Person.table_name).must_equal false
11
+ Person.auto_upgrade!
12
+ Person.table_name.must_equal 'people'
13
+ Person.db_columns.sort.must_equal %w[id name]
14
+ Person.column_names.sort.must_equal Person.db_columns
15
+ Person.column_names.sort.must_equal Person.schema_columns
16
+ person = Person.create(:name => 'foo')
17
+ person.name.must_equal 'foo'
18
+ proc { person.surname }.must_raise NoMethodError
19
+
20
+ # Add a column without lost data
21
+ Person.class_eval do
22
+ schema do |p|
23
+ p.string :name
24
+ p.string :surname
25
+ end
26
+ end
27
+ Person.auto_upgrade!
28
+ Person.count.must_equal 1
29
+ person = Person.last
30
+ person.name.must_equal 'foo'
31
+ person.surname.must_be_nil
32
+ person.update_attribute(:surname, 'bar')
33
+ Person.db_columns.sort.must_equal %w[id name surname]
34
+ Person.column_names.sort.must_equal Person.db_columns
35
+
36
+ # Remove a column without lost data
37
+ Person.class_eval do
38
+ schema do |p|
39
+ p.string :name
40
+ end
41
+ end
42
+ Person.auto_upgrade!
43
+ person = Person.last
44
+ person.name.must_equal 'foo'
45
+ proc { person.surname }.must_raise NoMethodError
46
+ Person.db_columns.sort.must_equal %w[id name]
47
+ Person.column_names.sort.must_equal Person.db_columns
48
+ Person.column_names.sort.must_equal Person.schema_columns
49
+
50
+ # Change column without lost data
51
+ Person.class_eval do
52
+ schema do |p|
53
+ p.text :name
54
+ end
55
+ end
56
+ person = Person.last
57
+ person.name.must_equal 'foo'
58
+ end
59
+
60
+ it 'has #key,col,property,attribute inside model' do
61
+ ActiveRecord::Base.connection.table_exists?(Post.table_name).must_equal false
62
+ ActiveRecord::Base.connection.table_exists?(Category.table_name).must_equal false
63
+ Post.auto_upgrade!; Category.auto_upgrade!
64
+ Post.column_names.sort.must_equal Post.db_columns
65
+ Category.column_names.sort.must_equal Category.schema_columns
66
+
67
+ # Check default properties
68
+ category = Category.create(:title => 'category')
69
+ post = Post.create(:title => 'foo', :body => 'bar', :category_id => category.id)
70
+ post = Post.first
71
+ post.title.must_equal 'foo'
72
+ post.body.must_equal 'bar'
73
+ post.category.must_equal category
74
+
75
+
76
+ # Remove a column
77
+ Post.reset_table_definition!
78
+ Post.class_eval do
79
+ col :name
80
+ col :category, :as => :references
81
+ end
82
+ Post.auto_upgrade!
83
+ post = Post.first
84
+ post.name.must_be_nil
85
+ post.category.must_equal category
86
+ post.wont_respond_to :title
87
+ end
88
+
89
+ it 'has indexes inside model' do
90
+ # Check indexes
91
+ Animal.auto_upgrade!
92
+ Animal.db_indexes.size.must_be :>, 0
93
+ Animal.db_indexes.must_equal Animal.indexes.keys.sort
94
+
95
+ indexes_was = Animal.db_indexes
96
+
97
+ # Remove an index
98
+ Animal.indexes.delete(indexes_was.pop)
99
+ Animal.auto_upgrade!
100
+ Animal.indexes.keys.sort.must_equal indexes_was
101
+ Animal.db_indexes.must_equal indexes_was
102
+
103
+ # Add a new index
104
+ Animal.class_eval do
105
+ col :category, :as => :references, :index => true
106
+ end
107
+ Animal.auto_upgrade!
108
+ Animal.db_columns.must_include "category_id"
109
+ Animal.db_indexes.must_equal((indexes_was << "index_animals_on_category_id").sort)
110
+ end
111
+
112
+ it 'works with STI' do
113
+ Pet.auto_upgrade!
114
+ Pet.reset_column_information
115
+ Pet.db_columns.must_include "type"
116
+ Dog.auto_upgrade!
117
+ Pet.db_columns.must_include "type"
118
+
119
+ # Now, let's we know if STI is working
120
+ Pet.create(:name => "foo")
121
+ Dog.create(:name => "bar")
122
+ Dog.count.must_equal 1
123
+ Dog.first.name.must_equal "bar"
124
+ Pet.count.must_equal 2
125
+ Pet.all.map(&:name).must_equal ["foo", "bar"]
126
+
127
+ # Check that this doesn't break things
128
+ Cat.auto_upgrade!
129
+ Dog.first.name.must_equal "bar"
130
+
131
+ # What's happen if we change schema?
132
+ Dog.table_definition.must_equal Pet.table_definition
133
+ Dog.indexes.must_equal Pet.indexes
134
+ Dog.class_eval do
135
+ col :bau
136
+ end
137
+ Dog.auto_upgrade!
138
+ Pet.db_columns.must_include "bau"
139
+ Dog.new.must_respond_to :bau
140
+ Cat.new.must_respond_to :bau
141
+ end
142
+
143
+ it 'works with custom inheritance column' do
144
+ User.auto_upgrade!
145
+ Administrator.create(:name => "Davide", :surname => "D'Agostino")
146
+ Customer.create(:name => "Foo", :surname => "Bar")
147
+ Administrator.count.must_equal 1
148
+ Administrator.first.name.must_equal "Davide"
149
+ Customer.count.must_equal 1
150
+ Customer.first.name.must_equal "Foo"
151
+ User.count.must_equal 2
152
+ User.first.role.must_equal "Administrator"
153
+ User.last.role.must_equal "Customer"
154
+ end
155
+
156
+ it 'allow multiple columns definitions' do
157
+ Fake.auto_upgrade!
158
+ Fake.create(:name => 'foo', :surname => 'bar', :category_id => 1, :group_id => 2)
159
+ fake = Fake.first
160
+ fake.name.must_equal 'foo'
161
+ fake.surname.must_equal 'bar'
162
+ fake.category_id.must_equal 1
163
+ fake.group_id.must_equal 2
164
+ end
165
+
166
+ it 'allows non-integer primary keys' do
167
+ Vegetable.auto_upgrade!
168
+ Vegetable.primary_key.must_equal 'latin_name'
169
+ end
170
+
171
+ it 'properly creates primary key columns so that ActiveRecord uses them' do
172
+ Vegetable.auto_upgrade!
173
+ Vegetable.delete_all
174
+ n = 'roobus roobious'
175
+ v = Vegetable.new; v.latin_name = n; v.save!
176
+ Vegetable.find(n).must_equal v
177
+ end
178
+
179
+ it 'automatically shortens long index names' do
180
+ AutomobileMakeModelYearVariant.auto_upgrade!
181
+ AutomobileMakeModelYearVariant.db_indexes.first.start_with?('index_automobile_make_model_ye').must_equal true
182
+ end
183
+
184
+ it 'properly creates primary key columns that are unique' do
185
+ Vegetable.auto_upgrade!
186
+ Vegetable.delete_all
187
+ n = 'roobus roobious'
188
+ v = Vegetable.new; v.latin_name = n; v.save!
189
+ if sqlite?
190
+ flunk # segfaults
191
+ # lambda { v = Vegetable.new; v.latin_name = n; v.save! }.must_raise(SQLite3::ConstraintException)
192
+ else
193
+ lambda { v = Vegetable.new; v.latin_name = n; v.save! }.must_raise(ActiveRecord::RecordNotUnique)
194
+ end
195
+ end
196
+
197
+ it 'is idempotent' do
198
+ ActiveRecord::Base.descendants.each do |active_record|
199
+ active_record.auto_upgrade!
200
+ active_record.reset_column_information
201
+ before = [ active_record.db_columns, active_record.db_indexes ]
202
+ active_record.auto_upgrade!
203
+ active_record.reset_column_information
204
+ [ active_record.db_columns, active_record.db_indexes ].must_equal before
205
+ active_record.auto_upgrade!
206
+ active_record.reset_column_information
207
+ active_record.auto_upgrade!
208
+ active_record.reset_column_information
209
+ active_record.auto_upgrade!
210
+ active_record.reset_column_information
211
+ [ active_record.db_columns, active_record.db_indexes ].must_equal before
212
+ end
213
+ end
214
+
215
+ private
216
+
217
+ def sqlite?
218
+ ActiveRecord::Base.connection.adapter_name =~ /sqlite/i
219
+ end
220
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'bundler/setup'
3
+ require 'mini_record'
4
+ require 'minitest/autorun'
5
+
6
+ module SpecHelper
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def db_columns
13
+ connection.columns(table_name).map(&:name).sort
14
+ end
15
+
16
+ def db_indexes
17
+ connection.indexes(table_name).map(&:name).sort
18
+ end
19
+
20
+ def schema_columns
21
+ table_definition.columns.map { |c| c.name.to_s }.sort
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+
5
+ # require 'logger'
6
+ # ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new($stdout)
7
+
8
+ require File.expand_path('../models.rb', __FILE__)
9
+
10
+ require File.expand_path('../shared_examples.rb', __FILE__)
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_record-compat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Davide D'Agostino
9
+ - Seamus Abshere
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-09-26 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ requirement: &2154134460 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *2154134460
26
+ description: ! "\nWith it you can add the ability to create columns outside the default
27
+ schema, directly\nin your model in a similar way that you just know in others projects\nlike
28
+ \ DataMapper or MongoMapper.\n "
29
+ email:
30
+ - d.dagostino@lipsiasoft.com
31
+ - seamus@abshere.net
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - .gitignore
37
+ - .travis.yml
38
+ - Gemfile
39
+ - README.md
40
+ - Rakefile
41
+ - lib/mini_record.rb
42
+ - lib/mini_record/auto_schema.rb
43
+ - lib/mini_record/version.rb
44
+ - mini_record.gemspec
45
+ - spec/models.rb
46
+ - spec/mysql_spec.rb
47
+ - spec/postgresql_spec.rb
48
+ - spec/shared_examples.rb
49
+ - spec/spec_helper.rb
50
+ - spec/sqlite3_spec.rb
51
+ homepage: https://github.com/seamusabshere/mini_record
52
+ licenses: []
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 1.8.6
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Alternate gem published by Seamus Abshere for ActiveRecord 3.0 support. MiniRecord
75
+ is a micro gem that allow you to write schema inside your model as you can do in
76
+ DataMapper.
77
+ test_files:
78
+ - spec/models.rb
79
+ - spec/mysql_spec.rb
80
+ - spec/postgresql_spec.rb
81
+ - spec/shared_examples.rb
82
+ - spec/spec_helper.rb
83
+ - spec/sqlite3_spec.rb