mini_record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mini_record.gemspec
4
+ gem "minitest"
5
+ gem "sqlite3"
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,113 @@
1
+ MiniRecord is micro extension for our `ActiveRecord` gem.
2
+ With it you can add the ability to create columns outside the default schema, directly
3
+ in your **model** in a similar way that you just know in others projects
4
+ like DataMapper or MongoMapper.
5
+
6
+ My inspiration come from this handy [project](https://github.com/pjhyett/auto_migrations)
7
+
8
+ ## Features
9
+
10
+ * Define columns/properties inside your model
11
+ * Perform migrations automatically
12
+ * Auto upgrade your schema, so if you know what you are doing you don't lost your existing data!
13
+ * Add, Remove, Change Columns; Add, Remove, Change indexes
14
+
15
+ ## Instructions
16
+
17
+ What you need is to move/remove `db/migrations` and `db/schema.rb`.
18
+ It's no more necessary and it avoid conflicts.
19
+
20
+ Add to your `Gemfile`:
21
+
22
+ ``` rb
23
+ gem 'mini_record'
24
+ ```
25
+
26
+ That's all!
27
+
28
+ ## Examples
29
+
30
+ Remember that inside properties you can use all migrations methods,
31
+ see [documentation](http://api.rubyonrails.org/classes/ActiveRecord/Migration.html)
32
+
33
+ ``` rb
34
+ class Person < ActiveRecord::Base
35
+ schema do |s|
36
+ s.string :name
37
+ s.integer :address_id
38
+ end
39
+ belongs_to :address
40
+ end
41
+
42
+ class Address < ActiveRecord::Base
43
+ schema, :id => true do |s| # id => true is not really necessary but as
44
+ s.string :city # in +create_table+ you have here the same options
45
+ s.string :state
46
+ s.integer :number
47
+ end
48
+ end
49
+ ```
50
+
51
+ Once you bootstrap your **app**, missing columns and tables will be created on the fly.
52
+
53
+ ### Adding a new column
54
+
55
+ Super easy, open your model and just add it:
56
+
57
+ ``` rb
58
+ class Person < ActiveRecord::Base
59
+ schema do |s|
60
+ s.string :name
61
+ s.string :surname # <<- this
62
+ s.integer :address_id
63
+ end
64
+ belongs_to :address
65
+ end
66
+ ```
67
+
68
+ So now when you start your **webserver** you can see an `ALTER TABLE` statement, this mean that your existing
69
+ records are happy and safe.
70
+
71
+ ### Removing a column
72
+
73
+ It's exactly the same, but the column will be _really_ deleted without affect other columns.
74
+
75
+ ### Changing a column
76
+
77
+ It's not possible for us know when/what column you have renamed, but we can know if you changed the `type` so
78
+ if you change `t.string :name` to `t.text :name` we are be able to perform an `ALTER TABLE`
79
+
80
+ ### Drop unused tables/indexes
81
+
82
+ You can do it by hand but if yours are lazy like mine you can simply invoke:
83
+
84
+ ``` rb
85
+ ActiveRecord::Base.drop_unused_tables
86
+ ActiveRecord::Base.drop_unused_indexes
87
+ ```
88
+
89
+ # Warning
90
+
91
+ This software is not yet tested in a production project, now is only heavy development and if you can
92
+ pleas fork it, find bug add a spec and then come back with a pull request. Thanks!
93
+
94
+
95
+ ## Author
96
+
97
+ 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)
98
+
99
+ ## Copyright
100
+
101
+ Copyright (C) 2011 Davide D'Agostino - [@daddye](http://twitter.com/daddye)
102
+
103
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
104
+ associated documentation files (the “Software”), to deal in the Software without restriction, including without
105
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
106
+ and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
107
+
108
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
109
+
110
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
111
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,
112
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
113
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
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 :default => :test
29
+ task :spec => :test
@@ -0,0 +1,108 @@
1
+ require 'active_support/core_ext/class/attribute_accessors'
2
+
3
+ module MiniRecord
4
+
5
+ module AutoMigrations
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ class << base
9
+ cattr_accessor :tables_in_schema, :indexes_in_schema
10
+ self.tables_in_schema, self.indexes_in_schema = [], []
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ def auto_create_table(table_name, options, &block)
16
+
17
+ (self.tables_in_schema ||= []) << table_name
18
+
19
+ # Table doesn't exist, create it
20
+ unless connection.tables.include?(table_name)
21
+ return connection.create_table(table_name, options, &block)
22
+ end
23
+
24
+ # Grab database columns
25
+ fields_in_db = connection.columns(table_name).inject({}) do |hash, column|
26
+ hash[column.name] = column
27
+ hash
28
+ end
29
+
30
+ # Grab schema columns (lifted from active_record/connection_adapters/abstract/schema_statements.rb)
31
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
32
+ primary_key = options[:primary_key] || "id"
33
+ table_definition.primary_key(primary_key) unless options[:id] == false
34
+
35
+ # Return the table definition
36
+ yield table_definition
37
+
38
+ # Grab new schema
39
+ fields_in_schema = table_definition.columns.inject({}) do |hash, column|
40
+ hash[column.name.to_s] = column
41
+ hash
42
+ end
43
+
44
+ # Remove fields from db no longer in schema
45
+ (fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
46
+ column = fields_in_db[field]
47
+ connection.remove_column table_name, column.name
48
+ end
49
+
50
+ # Add fields to db new to schema
51
+ (fields_in_schema.keys - fields_in_db.keys).each do |field|
52
+ column = fields_in_schema[field]
53
+ options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
54
+ options[:default] = column.default if !column.default.nil?
55
+ options[:null] = column.null if !column.null.nil?
56
+ connection.add_column table_name, column.name, column.type.to_sym, options
57
+ end
58
+
59
+ # Change attributes of existent columns
60
+ (fields_in_schema.keys & fields_in_db.keys).each do |field|
61
+ if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
62
+ changed = false # flag
63
+ new_type = fields_in_schema[field].type.to_sym
64
+ new_attr = {}
65
+
66
+ # First, check if the field type changed
67
+ if fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym
68
+ changed = true
69
+ end
70
+
71
+ # Special catch for precision/scale, since *both* must be specified together
72
+ # Always include them in the attr struct, but they'll only get applied if changed = true
73
+ new_attr[:precision] = fields_in_schema[field][:precision]
74
+ new_attr[:scale] = fields_in_schema[field][:scale]
75
+
76
+ # Next, iterate through our extended attributes, looking for any differences
77
+ # This catches stuff like :null, :precision, etc
78
+ fields_in_schema[field].each_pair do |att,value|
79
+ next if att == :type or att == :base or att == :name # special cases
80
+ if !value.nil? && value != fields_in_db[field].send(att)
81
+ new_attr[att] = value
82
+ changed = true
83
+ end
84
+ end
85
+
86
+ # Change the column if applicable
87
+ connection.change_column table_name, field, new_type, new_attr if changed
88
+ end
89
+ end
90
+ end
91
+
92
+ def drop_unused_tables
93
+ (connection.tables - tables_in_schema - %w(schema_info schema_migrations)).each do |table|
94
+ connection.drop_table table
95
+ end
96
+ end
97
+
98
+ def drop_unused_indexes
99
+ tables_in_schema.each do |table_name|
100
+ indexes_in_db = connection.indexes(table_name).map(&:name)
101
+ (indexes_in_db - indexes_in_schema & indexes_in_db).each do |index_name|
102
+ connection.remove_index table_name, :name => index_name
103
+ end
104
+ end
105
+ end
106
+ end # ClassMethods
107
+ end # Migrations
108
+ end # ActiveKey
@@ -0,0 +1,19 @@
1
+ require 'mini_record/auto_migrations'
2
+
3
+ module MiniRecord
4
+ module AutoSchema
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.send(:include, MiniRecord::AutoMigrations)
8
+ end
9
+
10
+ module ClassMethods
11
+ def schema(options={}, &block)
12
+ auto_create_table(table_name, options, &block)
13
+ reset_column_information
14
+ end
15
+ alias :keys :schema
16
+ alias :properties :schema
17
+ end
18
+ end # AutoSchema
19
+ end # MiniRecord
@@ -0,0 +1,3 @@
1
+ module MiniRecord
2
+ VERSION = "0.0.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"
7
+ s.version = MiniRecord::VERSION
8
+ s.authors = ["Davide D'Agostino"]
9
+ s.email = ["d.dagostino@lipsiasoft.com"]
10
+ s.homepage = "https://github.com/DAddYE/mini_record"
11
+ s.summary = %q{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
+ # specify any dependencies here; for example:
26
+ # s.add_development_dependency "rspec"
27
+ s.add_dependency "activerecord", "~>3.1.0"
28
+ end
@@ -0,0 +1,309 @@
1
+ !RBIX
2
+ 16846133056282117387
3
+ x
4
+ M
5
+ 1
6
+ n
7
+ n
8
+ x
9
+ 10
10
+ __script__
11
+ i
12
+ 86
13
+ 5
14
+ 7
15
+ 0
16
+ 64
17
+ 47
18
+ 49
19
+ 1
20
+ 1
21
+ 15
22
+ 5
23
+ 7
24
+ 2
25
+ 64
26
+ 47
27
+ 49
28
+ 1
29
+ 1
30
+ 15
31
+ 5
32
+ 7
33
+ 3
34
+ 64
35
+ 47
36
+ 49
37
+ 1
38
+ 1
39
+ 15
40
+ 45
41
+ 4
42
+ 5
43
+ 43
44
+ 6
45
+ 7
46
+ 7
47
+ 56
48
+ 8
49
+ 50
50
+ 9
51
+ 1
52
+ 15
53
+ 5
54
+ 44
55
+ 43
56
+ 10
57
+ 79
58
+ 49
59
+ 11
60
+ 1
61
+ 13
62
+ 7
63
+ 12
64
+ 7
65
+ 7
66
+ 49
67
+ 13
68
+ 2
69
+ 15
70
+ 47
71
+ 49
72
+ 14
73
+ 1
74
+ 15
75
+ 5
76
+ 44
77
+ 43
78
+ 10
79
+ 79
80
+ 49
81
+ 11
82
+ 1
83
+ 13
84
+ 7
85
+ 15
86
+ 7
87
+ 7
88
+ 49
89
+ 13
90
+ 2
91
+ 15
92
+ 47
93
+ 49
94
+ 14
95
+ 1
96
+ 15
97
+ 2
98
+ 11
99
+ I
100
+ 5
101
+ I
102
+ 0
103
+ I
104
+ 0
105
+ I
106
+ 0
107
+ n
108
+ p
109
+ 16
110
+ s
111
+ 17
112
+ bundler/gem_tasks
113
+ x
114
+ 7
115
+ require
116
+ s
117
+ 4
118
+ rake
119
+ s
120
+ 13
121
+ rake/testtask
122
+ x
123
+ 4
124
+ Rake
125
+ n
126
+ x
127
+ 8
128
+ TestTask
129
+ x
130
+ 4
131
+ test
132
+ M
133
+ 1
134
+ p
135
+ 2
136
+ x
137
+ 9
138
+ for_block
139
+ t
140
+ n
141
+ x
142
+ 9
143
+ __block__
144
+ i
145
+ 46
146
+ 57
147
+ 19
148
+ 0
149
+ 15
150
+ 20
151
+ 0
152
+ 49
153
+ 0
154
+ 0
155
+ 7
156
+ 1
157
+ 64
158
+ 49
159
+ 2
160
+ 1
161
+ 15
162
+ 20
163
+ 0
164
+ 45
165
+ 3
166
+ 4
167
+ 7
168
+ 5
169
+ 64
170
+ 49
171
+ 6
172
+ 1
173
+ 13
174
+ 18
175
+ 2
176
+ 49
177
+ 7
178
+ 1
179
+ 15
180
+ 15
181
+ 20
182
+ 0
183
+ 2
184
+ 13
185
+ 18
186
+ 2
187
+ 49
188
+ 8
189
+ 1
190
+ 15
191
+ 11
192
+ I
193
+ 5
194
+ I
195
+ 1
196
+ I
197
+ 1
198
+ I
199
+ 1
200
+ n
201
+ p
202
+ 9
203
+ x
204
+ 4
205
+ libs
206
+ s
207
+ 4
208
+ spec
209
+ x
210
+ 2
211
+ <<
212
+ x
213
+ 3
214
+ Dir
215
+ n
216
+ s
217
+ 17
218
+ spec/**/*_spec.rb
219
+ x
220
+ 2
221
+ []
222
+ x
223
+ 11
224
+ test_files=
225
+ x
226
+ 8
227
+ verbose=
228
+ p
229
+ 9
230
+ I
231
+ 0
232
+ I
233
+ 5
234
+ I
235
+ 4
236
+ I
237
+ 6
238
+ I
239
+ 10
240
+ I
241
+ 7
242
+ I
243
+ 23
244
+ I
245
+ 8
246
+ I
247
+ 2e
248
+ x
249
+ 42
250
+ /Developer/src/Extras/mini_record/rakefile
251
+ p
252
+ 1
253
+ x
254
+ 4
255
+ test
256
+ x
257
+ 3
258
+ new
259
+ x
260
+ 4
261
+ Hash
262
+ x
263
+ 16
264
+ new_from_literal
265
+ x
266
+ 7
267
+ default
268
+ x
269
+ 3
270
+ []=
271
+ x
272
+ 4
273
+ task
274
+ x
275
+ 4
276
+ spec
277
+ p
278
+ 13
279
+ I
280
+ 0
281
+ I
282
+ 1
283
+ I
284
+ 9
285
+ I
286
+ 2
287
+ I
288
+ 12
289
+ I
290
+ 3
291
+ I
292
+ 1b
293
+ I
294
+ 5
295
+ I
296
+ 28
297
+ I
298
+ b
299
+ I
300
+ 3e
301
+ I
302
+ c
303
+ I
304
+ 56
305
+ x
306
+ 42
307
+ /Developer/src/Extras/mini_record/rakefile
308
+ p
309
+ 0
@@ -0,0 +1,21 @@
1
+ require 'logger'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
4
+ # ActiveRecord::Base.connection.tables.each { |t| ActiveRecord::Base.connection.drop_table(t) }
5
+ # ActiveRecord::Base.logger = Logger.new($stdout)
6
+
7
+ class Person < ActiveRecord::Base
8
+ schema do |s|
9
+ s.string :name
10
+ end
11
+
12
+ # Testing purpose
13
+ def self.db_columns
14
+ connection.columns(table_name).map(&:name)
15
+ end
16
+
17
+ def self.schema_columns
18
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(connection)
19
+ table_definition.columns.map(&:name)
20
+ end
21
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../spec_helper.rb', __FILE__)
2
+ require File.expand_path('../fake_model.rb', __FILE__)
3
+
4
+ describe MiniRecord do
5
+
6
+ it 'works correctly' do
7
+ # For unknown reason separate specs doesn't works
8
+ Person.table_name.must_equal 'people'
9
+ Person.db_columns.must_equal %w[id name]
10
+ Person.column_names.must_equal Person.db_columns
11
+ person = Person.create(:name => 'foo')
12
+ person.name.must_equal 'foo'
13
+ proc { person.surname }.must_raise NoMethodError
14
+
15
+ # Add a column without lost data
16
+ Person.class_eval do
17
+ schema do |p|
18
+ p.string :name
19
+ p.string :surname
20
+ end
21
+ end
22
+ Person.count.must_equal 1
23
+ person = Person.last
24
+ person.name.must_equal 'foo'
25
+ person.surname.must_be_nil
26
+ person.update_attribute(:surname, 'bar')
27
+ Person.db_columns.must_equal %w[id name surname]
28
+ Person.column_names.must_equal Person.db_columns
29
+
30
+ # Remove a column without lost data
31
+ Person.class_eval do
32
+ schema do |p|
33
+ p.string :name
34
+ end
35
+ end
36
+ person = Person.last
37
+ person.name.must_equal 'foo'
38
+ proc { person.surname }.must_raise NoMethodError
39
+ Person.db_columns.must_equal %w[id name]
40
+ Person.column_names.must_equal Person.db_columns
41
+
42
+ # Change column without lost data
43
+ Person.class_eval do
44
+ schema do |p|
45
+ p.text :name
46
+ end
47
+ end
48
+ person = Person.last
49
+ person.name.must_equal 'foo'
50
+ end
51
+
52
+ it 'should remove no tables, since Person is still defined' do
53
+ ActiveRecord::Base.drop_unused_tables
54
+ ActiveRecord::Base.connection.tables.must_equal %w[people]
55
+ Person.count.must_equal 1
56
+ end
57
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'bundler/setup'
3
+ require 'mini_record'
4
+ require 'minitest/autorun'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_record
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Davide D'Agostino
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-05 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 3
32
+ - 1
33
+ - 0
34
+ version: 3.1.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: "\n\
38
+ With it you can add the ability to create columns outside the default schema, directly\n\
39
+ in your model in a similar way that you just know in others projects\n\
40
+ like DataMapper or MongoMapper.\n "
41
+ email:
42
+ - d.dagostino@lipsiasoft.com
43
+ executables: []
44
+
45
+ extensions: []
46
+
47
+ extra_rdoc_files: []
48
+
49
+ files:
50
+ - .gitignore
51
+ - Gemfile
52
+ - README.md
53
+ - Rakefile
54
+ - lib/mini_record.rb
55
+ - lib/mini_record/auto_migrations.rb
56
+ - lib/mini_record/auto_schema.rb
57
+ - lib/mini_record/version.rb
58
+ - mini_record.gemspec
59
+ - rakefile.compiled.rbc
60
+ - spec/fake_model.rb
61
+ - spec/mini_record_spec.rb
62
+ - spec/spec_helper.rb
63
+ has_rdoc: true
64
+ homepage: https://github.com/DAddYE/mini_record
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options: []
69
+
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project: mini_record
93
+ rubygems_version: 1.6.2
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: MiniRecord is a micro gem that allow you to write schema inside your model as you can do in DataMapper.
97
+ test_files:
98
+ - spec/fake_model.rb
99
+ - spec/mini_record_spec.rb
100
+ - spec/spec_helper.rb