mini_record 0.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 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