schemer 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Ben Alavi for Citrusbyte
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,120 @@
1
+ Schemer
2
+ =======
3
+
4
+ On-the-fly ActiveRecord schema changes for extremely rapid prototyping.
5
+
6
+ Description
7
+ -----------
8
+
9
+ Loosely define your schema in your ActiveRecord model and have it created and
10
+ updated for you without worrying about migrations. Useful for when you want to
11
+ play around with real data handling during prototyping but you really don't
12
+ care about keeping the data or how it's defined.
13
+
14
+ This isn't meant to be a replacement for migrations, it is simply a way to get
15
+ started on a project quickly while you iron out your data definitions.
16
+
17
+ **WARNING:** This will create and delete data definitions on the fly with no
18
+ warning! Only use this with volatile data! Never attach it to an existing model
19
+ you care about as it will start adding and dropping columns without your
20
+ consent!
21
+
22
+ Usage
23
+ -----
24
+
25
+ class User < ActiveRecord::Base
26
+ schema :username, :password
27
+ end
28
+
29
+ Creates a `users` table if it doesn't exist, complete with `username` and
30
+ `password` columns the first time the User class is loaded.
31
+
32
+ Need another column to play with?
33
+
34
+ class User < ActiveRecord::Base
35
+ schema :username, :password, :email
36
+ end
37
+
38
+ Adds an `email` column the next time `User` class is loaded.
39
+
40
+ Want a relationship? (but fear commitment...)
41
+
42
+ class Widget < ActiveRecord::Base
43
+ schema :user_id, :name
44
+
45
+ belongs_to :user
46
+ end
47
+
48
+ Works just fine, and you can drop it at any time!
49
+
50
+ class Widget < ActiveRecord::Base
51
+ schema :name
52
+ end
53
+
54
+ Removes the `user_id` column the next time the `Widget` class is loaded.
55
+
56
+ Have a need for a particular type of column?
57
+
58
+ class Widget < ActiveRecord::Base
59
+ schema :name, { :size => :integer }, :description
60
+ end
61
+
62
+ Will create `size` as an `:integer` column so you can take advantage of Rails'
63
+ casting.
64
+
65
+ Feeling more confident and ready to make that big leap to migrations? Just run:
66
+
67
+ rake schemer:migration
68
+
69
+ To get:
70
+
71
+ Migration from schema declarations in User, Widget
72
+
73
+ create_table :users do |t|
74
+ t.string :username
75
+ t.string :password
76
+ t.string :email
77
+ end
78
+
79
+ create_table :widgets do |t|
80
+ t.string :name
81
+ t.integer :size
82
+ t.string :description
83
+ end
84
+
85
+ Then you can paste the output into a migration file, setup your columns (don't
86
+ forget your indexes!) and get rid of your `schema` calls in the listed classes
87
+ and you're on your way to the big leagues!
88
+
89
+ **NOTE:** All columns are created as string columns unless type is given.
90
+
91
+ Installation
92
+ ------------
93
+
94
+ $ sudo gem install citrusbyte-schemer --source=http://gems.github.com
95
+
96
+ License
97
+ -------
98
+
99
+ Copyright (c) 2009 Ben Alavi for Citrusbyte
100
+
101
+ Permission is hereby granted, free of charge, to any person
102
+ obtaining a copy of this software and associated documentation
103
+ files (the "Software"), to deal in the Software without
104
+ restriction, including without limitation the rights to use,
105
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
106
+ copies of the Software, and to permit persons to whom the
107
+ Software is furnished to do so, subject to the following
108
+ conditions:
109
+
110
+ The above copyright notice and this permission notice shall be
111
+ included in all copies or substantial portions of the Software.
112
+
113
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
114
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
115
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
116
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
117
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
118
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
119
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
120
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/clean'
5
+
6
+ gem_spec_file = 'schemer.gemspec'
7
+
8
+ gem_spec = eval(File.read(gem_spec_file)) rescue nil
9
+
10
+ task :default => :test
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = false
15
+ end
16
+
17
+ Rake::GemPackageTask.new(gem_spec) do |pkg|
18
+ pkg.need_zip = false
19
+ pkg.need_tar = false
20
+ rm_f FileList['pkg/**/*.*']
21
+ end if gem_spec
22
+
23
+ desc "Generate the gemspec file."
24
+ task :gemspec do
25
+ require 'erb'
26
+
27
+ File.open(gem_spec_file, 'w') do |f|
28
+ f.write ERB.new(File.read("#{gem_spec_file}.erb")).result(binding)
29
+ end
30
+ end
31
+
32
+ desc "Builds and installs the gem."
33
+ task :install => :repackage do
34
+ `sudo gem install pkg/#{gem_spec.name}-#{gem_spec.version}.gem`
35
+ end
data/lib/schemer.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'activerecord'
2
+
3
+ module Schemer
4
+ def schema(*args)
5
+ extend ClassMethods
6
+
7
+ class_inheritable_accessor :schema_columns
8
+ self.schema_columns = {}
9
+ args.collect{ |a| a.is_a?(Hash) ? a.stringify_keys : { a.to_s => :string } }.each do |column|
10
+ self.schema_columns.merge!(column)
11
+ end
12
+
13
+ update_schema
14
+ update_methods
15
+ end
16
+
17
+ module ClassMethods
18
+ # Columns which we don't touch regardless of not being defined in schema
19
+ # (and are ignored if they are defined in schema)
20
+ def protected_columns
21
+ %w( id )
22
+ end
23
+
24
+ # Create the underlying table for this class
25
+ def create_table
26
+ ActiveRecord::Migration.create_table(table_name) do |t|;end;
27
+ end
28
+
29
+ # Update ActiveRecord's automatically generated methods so we don't have to
30
+ # reload for schema changes to take effect
31
+ def update_methods
32
+ generated_methods.each { |method| remove_method(method) }
33
+ @columns = @column_names = @columns_hash = @generated_methods = nil
34
+ end
35
+
36
+ # Update the underlying schema as defined by schema call
37
+ def update_schema
38
+ create_table unless table_exists?
39
+
40
+ columns.reject{ |column| protected_columns.include?(column.name) }.each do |column|
41
+ if !schema_columns.has_key?(column.name)
42
+ # remove any extraneous columns
43
+ ActiveRecord::Migration.remove_column(table_name, column.name)
44
+ elsif column.type != schema_columns[column.name]
45
+ # change any columns w/ wrong type
46
+ ActiveRecord::Migration.change_column(table_name, column.name, schema_columns[column.name])
47
+ end
48
+ end
49
+
50
+ # add any missing columns
51
+ (schema_columns.keys - column_names).each do |column|
52
+ ActiveRecord::Migration.add_column(table_name, column, schema_columns[column])
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ ActiveRecord::Base.extend(Schemer)
@@ -0,0 +1,18 @@
1
+ module Schemer
2
+ class Migrator
3
+ class << self
4
+ # Outputs the Rails migration for the schema defined in the given class.
5
+ #
6
+ # Outputs an empty string if no schema is defined on the class.
7
+ def migration(klass)
8
+ return nil unless klass.respond_to?(:schema_columns)
9
+
10
+ "create_table :#{klass.table_name} do |t|\n" +
11
+ (klass.schema_columns.keys - klass.protected_columns).collect do |column|
12
+ " t.#{klass.schema_columns[column]} :#{column}"
13
+ end.join("\n") +
14
+ "\nend"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ namespace :schemer do
2
+ desc "Outputs the basis of a Rails migration from your schema declarations"
3
+ task :migration => :environment do
4
+ klasses = []
5
+
6
+ Dir.glob(File.join(Rails.root, "app", "models", "**.rb")).each do |file|
7
+ lineno = 0
8
+ result = File.readlines(file).each do |line|
9
+ lineno += 1
10
+ next unless line =~ /^class (.*) \<.*$/
11
+ klasses << $1.constantize
12
+ end
13
+ end
14
+
15
+ migration = klasses.collect do |klass|
16
+ Schemer::Migrator.migration(klass)
17
+ end.join("\n\n")
18
+
19
+ puts "\nMigration from schema declarations in #{klasses.join(', ')}"
20
+ puts "\n#{migration}\n\n"
21
+ end
22
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ raise "We highly suggest you only use Schemer in development or test mode (not in #{Rails.env})!" unless %w( development test ).include?(Rails.env.to_s)
2
+ Rails.logger.warn "Loading Schemer which will consider all your data volatile!"
3
+ require 'schemer'
@@ -0,0 +1,94 @@
1
+ require 'rubygems'
2
+ require 'contest'
3
+ require 'override'
4
+ require 'ruby-debug'
5
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'schemer')
6
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'schemer', 'migrator')
7
+
8
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => 'test/schemer_test.db'
9
+ ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
10
+
11
+ class Foo < ActiveRecord::Base;end;
12
+ ActiveRecord::Migration.drop_table(Foo.table_name) if Foo.table_exists?
13
+ class Bar < ActiveRecord::Base;end;
14
+ ActiveRecord::Migration.drop_table(Bar.table_name) if Bar.table_exists?
15
+
16
+ class FooTest < Test::Unit::TestCase
17
+ context "defining the schema" do
18
+ setup do
19
+ Foo.schema :foo, :bar
20
+ end
21
+
22
+ should "create the foos table" do
23
+ assert Foo.table_exists?
24
+ end
25
+
26
+ context "with an instance" do
27
+ setup do
28
+ @foo = Foo.new
29
+ end
30
+
31
+ should "create the foo column" do
32
+ assert @foo.respond_to?(:foo)
33
+ end
34
+
35
+ should "create the bar column" do
36
+ assert @foo.respond_to?(:bar)
37
+ end
38
+ end
39
+ end
40
+
41
+ context "updating the schema" do
42
+ setup do
43
+ Foo.schema :foo, :bar
44
+ Foo.schema :foo
45
+ @foo = Foo.new
46
+ end
47
+
48
+ should "remove the bar column" do
49
+ assert !@foo.respond_to?(:bar)
50
+ end
51
+ end
52
+
53
+ context "with types" do
54
+ setup do
55
+ Foo.schema :foo, { :bar => :integer }, :baz
56
+ @foo = Foo.find(Foo.create!(:foo => '5', :bar => 5).id)
57
+ end
58
+
59
+ should "create foo, bar and baz columns" do
60
+ assert @foo.respond_to?(:foo)
61
+ assert @foo.respond_to?(:bar)
62
+ assert @foo.respond_to?(:baz)
63
+ end
64
+
65
+ should "create bar column using integer datatype" do
66
+ assert_equal 5, @foo.bar
67
+ end
68
+
69
+ should "create foo column using string datatype" do
70
+ assert_equal '5', @foo.foo
71
+ end
72
+
73
+ should "recreate foo column as integer" do
74
+ Foo.schema({ :foo => :integer }, { :bar => :integer }, :baz)
75
+ foo = Foo.find(Foo.create!(:foo => '5', :bar => '5').id)
76
+ assert_equal 5, foo.foo
77
+ end
78
+ end
79
+
80
+ context "building a Rails migration" do
81
+ setup do
82
+ Foo.schema :foo, :bar
83
+ end
84
+
85
+ should "output the migration for Foo" do
86
+ assert_equal(
87
+ %Q{create_table :foos do |t|
88
+ t.string :foo
89
+ t.string :bar
90
+ end}, Schemer::Migrator.migration(Foo)
91
+ )
92
+ end
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schemer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.10
5
+ platform: ruby
6
+ authors:
7
+ - Ben Alavi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-05 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: ben.alavi@citrusbyte.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.markdown
26
+ - LICENSE
27
+ - Rakefile
28
+ - lib/schemer/migrator.rb
29
+ - lib/schemer.rb
30
+ - lib/schemer/tasks/schemer.rake
31
+ - rails/init.rb
32
+ - test/schemer_test.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/citrusbyte/schemer
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: On-the-fly ActiveRecord schema changes for extremely rapid prototyping.
61
+ test_files: []
62
+