schemer 0.0.10

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/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
+