normalize_it 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec", "~> 2.3.0"
5
+ gem "bundler", "~> 1.0.0"
6
+ gem "jeweler", "~> 1.5.2"
7
+ gem "rcov", ">= 0"
8
+ end
9
+
10
+ group :test do
11
+ gem 'rspec', '~> 2.3.0'
12
+ gem 'rspec-rails'
13
+ gem 'activerecord', '~> 3.0.0'
14
+ gem 'sqlite3'
15
+ end
@@ -0,0 +1,77 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionpack (3.0.9)
6
+ activemodel (= 3.0.9)
7
+ activesupport (= 3.0.9)
8
+ builder (~> 2.1.2)
9
+ erubis (~> 2.6.6)
10
+ i18n (~> 0.5.0)
11
+ rack (~> 1.2.1)
12
+ rack-mount (~> 0.6.14)
13
+ rack-test (~> 0.5.7)
14
+ tzinfo (~> 0.3.23)
15
+ activemodel (3.0.9)
16
+ activesupport (= 3.0.9)
17
+ builder (~> 2.1.2)
18
+ i18n (~> 0.5.0)
19
+ activerecord (3.0.9)
20
+ activemodel (= 3.0.9)
21
+ activesupport (= 3.0.9)
22
+ arel (~> 2.0.10)
23
+ tzinfo (~> 0.3.23)
24
+ activesupport (3.0.9)
25
+ arel (2.0.10)
26
+ builder (2.1.2)
27
+ diff-lcs (1.1.2)
28
+ erubis (2.6.6)
29
+ abstract (>= 1.0.0)
30
+ git (1.2.5)
31
+ i18n (0.5.0)
32
+ jeweler (1.5.2)
33
+ bundler (~> 1.0.0)
34
+ git (>= 1.2.5)
35
+ rake
36
+ rack (1.2.3)
37
+ rack-mount (0.6.14)
38
+ rack (>= 1.0.0)
39
+ rack-test (0.5.7)
40
+ rack (>= 1.0)
41
+ railties (3.0.9)
42
+ actionpack (= 3.0.9)
43
+ activesupport (= 3.0.9)
44
+ rake (>= 0.8.7)
45
+ rdoc (~> 3.4)
46
+ thor (~> 0.14.4)
47
+ rake (0.9.2)
48
+ rcov (0.9.9)
49
+ rdoc (3.9.2)
50
+ rspec (2.3.0)
51
+ rspec-core (~> 2.3.0)
52
+ rspec-expectations (~> 2.3.0)
53
+ rspec-mocks (~> 2.3.0)
54
+ rspec-core (2.3.1)
55
+ rspec-expectations (2.3.0)
56
+ diff-lcs (~> 1.1.2)
57
+ rspec-mocks (2.3.0)
58
+ rspec-rails (2.3.1)
59
+ actionpack (~> 3.0)
60
+ activesupport (~> 3.0)
61
+ railties (~> 3.0)
62
+ rspec (~> 2.3.0)
63
+ sqlite3 (1.3.3)
64
+ thor (0.14.6)
65
+ tzinfo (0.3.29)
66
+
67
+ PLATFORMS
68
+ ruby
69
+
70
+ DEPENDENCIES
71
+ activerecord (~> 3.0.0)
72
+ bundler (~> 1.0.0)
73
+ jeweler (~> 1.5.2)
74
+ rcov
75
+ rspec (~> 2.3.0)
76
+ rspec-rails
77
+ sqlite3
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Dave Havlicek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,70 @@
1
+ = normalize_it
2
+
3
+ normalize_it makes it easy to seamlessly manage database tables that have been normalized.
4
+
5
+ Example:
6
+
7
+ create_table :customer_statuses, :force => true do |t|
8
+ t.string :customer_status
9
+ end
10
+
11
+ create_table :email_addresses, :force => true do |t|
12
+ t.string :email_address
13
+ end
14
+
15
+ create_table :customers, :force => true do |t|
16
+ t.string :name
17
+ t.integer :customer_status_id
18
+ t.integer :email_address_id
19
+ end
20
+
21
+ class CustomerStatus < ActiveRecord::Base
22
+ normalizes :customers, :with_field => :customer_status
23
+ validates :customer_status, :presence => true
24
+ validates_uniqueness_of :customer_status
25
+ end
26
+
27
+ class EmailAddress < ActiveRecord::Base
28
+ normalizes :customers, :allow_inserts => true
29
+ validates :email_address, :presence => true
30
+ validates_uniqueness_of :email_address
31
+ end
32
+
33
+ class Customer < ActiveRecord::Base
34
+ has_normalized :customer_status
35
+ has_normalized :email_address, :allow_inserts => true
36
+ end
37
+
38
+ after calling normalizes:
39
+ * if :allow_inserts is false (default), a :with_field argument must be passed in
40
+ ** new objects of CustomerStatus may not be created by the app
41
+ ** CustomerStatus objects may be referenced by [] notation, eg CustomerStatus[:new] or CustomerStatus['new']
42
+ ** CustomerStatus is given a has_many association to Customer, has_many options may be passed in to normalizes
43
+ * if :allow_inserts is true
44
+ ** only the has_many association is set up
45
+ ** [] notation may be used only if the :with_field option is passed
46
+
47
+ after calling has_normalized:
48
+ * if :allow_inserts is false (default)
49
+ ** customer.customer_status = 'new' will only search for CustomerStatus['new'] and assign it if it is found
50
+ * if :allow_inserts is true
51
+ ** customer.email_address = 'x@example.com' will search for the email address, and create it if not found
52
+ * all columns on the normalized tables are delegated to the parent object. e.g. customer.email_address
53
+ * static rails-like finders may be used. e.g. Customer.find_by_email_address 'x@example.com'
54
+ * new objects may be initialized either with attributes or through later assignment. e.g. Customer.new(:email_address => 'x@example.com')
55
+
56
+ == Contributing to normalize_it
57
+
58
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
59
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
60
+ * Fork the project
61
+ * Start a feature/bugfix branch
62
+ * Commit and push until you are happy with your contribution
63
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
64
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
65
+
66
+ == Copyright
67
+
68
+ Copyright (c) 2011 Dave Havlicek. See LICENSE.txt for
69
+ further details.
70
+
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "normalize_it"
16
+ gem.homepage = "http://github.com/snex/normalize_it"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{normalize_it makes it easy to seamlessly manage database tables that have been normalized.}
19
+ gem.description = %Q{Seamlessly deal with database tables with normalized info. Auto-generation of relationships
20
+ and delegators.}
21
+ gem.email = "xens@comcast.net"
22
+ gem.authors = ["Dave Havlicek"]
23
+ gem.files.exclude '.rvmrc'
24
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
25
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
26
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
27
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rspec/core'
32
+ require 'rspec/core/rake_task'
33
+ RSpec::Core::RakeTask.new(:spec) do |spec|
34
+ spec.pattern = FileList['spec/**/*_spec.rb']
35
+ end
36
+
37
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :default => :spec
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "normalize_it #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'normalize_it'
@@ -0,0 +1,168 @@
1
+ module NormalizeIt
2
+
3
+ class NormalizeItException < Exception ; end
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ #normalize_it makes it easy to seamlessly manage database tables that have been normalized.
10
+ #
11
+ #Example:
12
+ #
13
+ # create_table :customer_statuses, :force => true do |t|
14
+ # t.string :customer_status
15
+ # end
16
+ #
17
+ # create_table :email_addresses, :force => true do |t|
18
+ # t.string :email_address
19
+ # end
20
+ #
21
+ # create_table :customers, :force => true do |t|
22
+ # t.string :name
23
+ # t.integer :customer_status_id
24
+ # t.integer :email_address_id
25
+ # end
26
+ #
27
+ # class CustomerStatus < ActiveRecord::Base
28
+ # normalizes :customers, :with_field => :customer_status
29
+ # validates :customer_status, :presence => true
30
+ # validates_uniqueness_of :customer_status
31
+ # end
32
+ #
33
+ # class EmailAddress < ActiveRecord::Base
34
+ # normalizes :customers, :allow_inserts => true
35
+ # validates :email_address, :presence => true
36
+ # validates_uniqueness_of :email_address
37
+ # end
38
+ #
39
+ # class Customer < ActiveRecord::Base
40
+ # has_normalized :customer_status
41
+ # has_normalized :email_address, :allow_inserts => true
42
+ # end
43
+ #
44
+ # after calling normalizes:
45
+ # * if :allow_inserts is false (default), a :with_field argument must be passed in
46
+ # ** new objects of CustomerStatus may not be created by the app
47
+ # ** CustomerStatus objects may be referenced by [] notation, eg CustomerStatus[:new] or CustomerStatus['new']
48
+ # ** CustomerStatus is given a has_many association to Customer, has_many options may be passed in to normalizes
49
+ # * if :allow_inserts is true
50
+ # ** only the has_many association is set up
51
+ # ** [] notation may be used only if the :with_field option is passed
52
+ #
53
+ # after calling has_normalized:
54
+ # * if :allow_inserts is false (default)
55
+ # ** customer.customer_status = 'new' will only search for CustomerStatus['new'] and assign it if it is found
56
+ # * if :allow_inserts is true
57
+ # ** customer.email_address = 'x@example.com' will search for the email address, and create it if not found
58
+ # * all columns on the normalized tables are delegated to the parent object. e.g. customer.email_address
59
+ # * static rails-like finders may be used. e.g. Customer.find_by_email_address 'x@example.com'
60
+ # * new objects may be initialized either with attributes or through later assignment. e.g. Customer.new(:email_address => 'x@example.com')
61
+
62
+ module ClassMethods
63
+ attr_reader :_normalized_models
64
+
65
+ def has_normalized(model, options = {})
66
+ include NormalizeIt::BaseClassMethods
67
+ @_normalized_models ||= []
68
+ allow_inserts = options.try(:delete, :allow_inserts) || false
69
+ opts = { :class_name => model.to_s.camelize,
70
+ :autosave => true,
71
+ :foreign_key => model.to_s.foreign_key }.merge!(options)
72
+ belongs_to "__#{opts[:class_name].underscore}".to_sym, opts
73
+ validates "__#{opts[:class_name].underscore}".to_sym, :presence => true
74
+ validates_associated "__#{opts[:class_name].underscore}".to_sym
75
+ @_normalized_models << opts.merge( { :allow_inserts => allow_inserts } )
76
+ opts[:class_name].constantize.content_columns.each do |column|
77
+ next if ['created_at', 'updated_at'].include?(column.name)
78
+ delegate "#{column.name}", "#{column.name}?", :to => "__#{opts[:class_name].underscore}".to_sym
79
+ if allow_inserts
80
+ delegate "#{column.name}=", :to => "__#{opts[:class_name].underscore}".to_sym
81
+ else
82
+ self.class_eval do
83
+ define_method "#{column.name}=" do |value|
84
+ self.send("__#{opts[:class_name].underscore}=",
85
+ value.is_a?(opts[:class_name].constantize) ? value :
86
+ opts[:class_name].constantize[value]
87
+ )
88
+ end
89
+ end
90
+ end
91
+ eval <<-NEWFINDERS
92
+ def self.find_by_#{column.name} value
93
+ self.send("find_by_#{opts[:foreign_key]}",
94
+ #{opts[:class_name]}.send("find_by_#{column.name}", value)
95
+ )
96
+ end
97
+ def self.find_all_by_#{column.name} value
98
+ self.send("find_all_by_#{opts[:foreign_key]}",
99
+ #{opts[:class_name]}.send("find_by_#{column.name}", value)
100
+ )
101
+ end
102
+ NEWFINDERS
103
+ end
104
+
105
+ before_validation :handle_normalized_objects if allow_inserts
106
+ end
107
+
108
+ def normalizes(model, options = {})
109
+ include NormalizeIt::NormalizedClassMethods
110
+ allow_inserts = options.try(:delete, :allow_inserts) || false
111
+ if !allow_inserts
112
+ normalized_field = options.try(:delete, :with_field)
113
+ raise NormalizeItException, "Normalized field must be specified on static tables!" unless normalized_field
114
+ before_create :disallow_create unless allow_inserts
115
+ if normalized_field
116
+ eval <<-HASHNOTATIONACCESS
117
+ def self.[] value
118
+ @@#{self.name.underscore.pluralize} ||= HashWithIndifferentAccess.new
119
+ @@#{self.name.underscore.pluralize}[value] ||= find_by_#{normalized_field.to_s}(value.to_s)
120
+ @@#{self.name.underscore.pluralize}[value]
121
+ end
122
+ HASHNOTATIONACCESS
123
+ end
124
+ end
125
+ has_many model, options
126
+ end
127
+ end
128
+
129
+ module BaseClassMethods
130
+ attr_reader :_normalized_attributes
131
+
132
+ def initialize(attributes = {})
133
+ @_normalized_attributes = {}
134
+ self.class._normalized_models.each do |model_hash|
135
+ @_normalized_attributes[model_hash[:class_name].underscore.to_sym] ||= {}
136
+ attributes.each do |key, value|
137
+ if key.to_s.foreign_key == model_hash[:foreign_key]
138
+ @_normalized_attributes[model_hash[:class_name].underscore.to_sym][key] = attributes.delete(key)
139
+ end
140
+ end
141
+ end
142
+ super
143
+ @_normalized_attributes.each do |klass, attribs|
144
+ self.send("__#{klass.to_s}=", klass.to_s.camelize.constantize.new(attribs))
145
+ end
146
+ end
147
+
148
+ def handle_normalized_objects
149
+ self.transaction do
150
+ @_normalized_attributes.each do |klass, attribs|
151
+ existing_obj = klass.to_s.camelize.constantize.where( attribs ).first
152
+ if existing_obj
153
+ self.send("__#{klass.to_s}=", existing_obj)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ module NormalizedClassMethods
161
+ def disallow_create
162
+ errors.add :base, "Creation of #{self.class} not allowed!"
163
+ false
164
+ end
165
+ end
166
+ end
167
+
168
+ ActiveRecord::Base.send :include, NormalizeIt
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{normalize_it}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dave Havlicek"]
12
+ s.date = %q{2011-08-22}
13
+ s.description = %q{Seamlessly deal with database tables with normalized info. Auto-generation of relationships
14
+ and delegators.}
15
+ s.email = %q{xens@comcast.net}
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "init.rb",
30
+ "lib/normalize_it.rb",
31
+ "normalize_it.gemspec",
32
+ "spec/normalize_it_spec.rb",
33
+ "spec/schema.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/snex/normalize_it}
37
+ s.licenses = ["MIT"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.6.0}
40
+ s.summary = %q{normalize_it makes it easy to seamlessly manage database tables that have been normalized.}
41
+ s.test_files = [
42
+ "spec/normalize_it_spec.rb",
43
+ "spec/schema.rb",
44
+ "spec/spec_helper.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
52
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
54
+ s.add_development_dependency(%q<rcov>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
57
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
59
+ s.add_dependency(%q<rcov>, [">= 0"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
63
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
64
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
65
+ s.add_dependency(%q<rcov>, [">= 0"])
66
+ end
67
+ end
68
+
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "NormalizeIt" do
4
+ describe "classes" do
5
+ it 'should hook in to AR:Base' do
6
+ ActiveRecord::Base.should respond_to :has_normalized
7
+ ActiveRecord::Base.should respond_to :normalizes
8
+ end
9
+
10
+ it "should error if a static lookup class is defined without specifying which field is being normalized" do
11
+ expect { class NewField < ActiveRecord::Base
12
+ normalizes :customers, :allow_inserts => false
13
+ end
14
+ }.should raise_error NormalizeIt::NormalizeItException
15
+ end
16
+ end
17
+
18
+ describe "instances" do
19
+ it "should not allow creating a new customer_status" do
20
+ customer_status = CustomerStatus.new(:customer_status => 'old')
21
+ customer_status.save
22
+ customer_status.errors.should == {:base=>["Creation of CustomerStatus not allowed!"]}
23
+ expect { customer_status.save! }.should raise_error ActiveRecord::RecordNotSaved
24
+ end
25
+
26
+ it "should create a new customer by passing in normalized values through attributes" do
27
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
28
+ customer.save
29
+ Customer.should have(1).records
30
+ customer.email_address.should == 'xens@comcast.net'
31
+ customer.customer_status.should == 'new'
32
+ end
33
+
34
+ it "should create a new customer by calling new and then assigning attributes" do
35
+ customer = Customer.new(:name => 'Dave Havlicek')
36
+ customer.email_address = 'xens@comcast.net'
37
+ customer.customer_status = 'new'
38
+ customer.save
39
+ Customer.should have(1).records
40
+ customer.email_address.should == 'xens@comcast.net'
41
+ customer.customer_status.should == 'new'
42
+ end
43
+
44
+ it "should use already-existing objects for attributes" do
45
+ email_address = EmailAddress.create(:email_address => 'xens@comcast.net')
46
+ customer_status = CustomerStatus.find_by_customer_status 'new'
47
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
48
+ customer.save
49
+ Customer.should have(1).records
50
+ customer.email_address_id.should == email_address.id
51
+ customer.customer_status_id.should == customer_status.id
52
+ end
53
+
54
+ it "should not fail if normalized objects are created before the original object is saved" do
55
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
56
+ email_address = EmailAddress.create(:email_address => 'xens@comcast.net')
57
+ customer_status = CustomerStatus.find_by_customer_status 'new'
58
+ customer.save
59
+ Customer.should have(1).records
60
+ customer.email_address_id.should == email_address.id
61
+ customer.customer_status_id.should == customer_status.id
62
+ end
63
+
64
+ it "should be able to use a rails-like finder to find objects by normalized attributes" do
65
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
66
+ customer.save
67
+ customers = Customer.find_all_by_email_address 'xens@comcast.net'
68
+ customers.size.should == 1
69
+ customers.first.should == customer
70
+ end
71
+
72
+ it "should be able to combine rails-like finders" do
73
+ pending "it would be nice to have this feature someday" do
74
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
75
+ customer.save
76
+ customer_1 = Customer.find_by_email_address_and_customer_status_and_name 'xens@comcast.net', 'new', 'Dave Havlicek'
77
+ customer_1.should == customer
78
+ end
79
+ end
80
+
81
+ it "should not break original rails finders" do
82
+ customer = Customer.new(:name => 'Dave Havlicek', :email_address => 'xens@comcast.net', :customer_status => 'new')
83
+ customer.save
84
+ customer_1 = Customer.find_by_name 'Dave Havlicek'
85
+ customer_1.should == customer
86
+ end
87
+
88
+ it "should allow referencing static lookup models with [] notation" do
89
+ customer_status = CustomerStatus.find_by_customer_status 'new'
90
+ CustomerStatus[:new].should == customer_status
91
+ CustomerStatus['new'].should == customer_status
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,15 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :customer_statuses, :force => true do |t|
3
+ t.string :customer_status
4
+ end
5
+
6
+ create_table :email_addresses, :force => true do |t|
7
+ t.string :email_address
8
+ end
9
+
10
+ create_table :customers, :force => true do |t|
11
+ t.string :name
12
+ t.integer :customer_status_id
13
+ t.integer :email_address_id
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require 'rspec/rails/extensions'
7
+ require 'rspec/rails/matchers'
8
+ require 'rspec/rails/adapters'
9
+ require 'rspec/rails/fixture_support'
10
+ require 'normalize_it'
11
+
12
+ # Requires supporting files with custom matchers and macros, etc,
13
+ # in ./support/ and its subdirectories.
14
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
15
+
16
+ RSpec.configure do |config|
17
+ config.use_transactional_examples = true
18
+ end
19
+
20
+ ActiveRecord::Base.configurations={'test' => {:adapter => "sqlite3", :database => ":memory:"} }
21
+ ActiveRecord::Base.establish_connection('test')
22
+ load(File.dirname(__FILE__) + "/schema.rb")
23
+
24
+ class CustomerStatus < ActiveRecord::Base
25
+ normalizes :customers, :with_field => :customer_status
26
+ validates :customer_status, :presence => true
27
+ validates_uniqueness_of :customer_status
28
+ end
29
+
30
+ class EmailAddress < ActiveRecord::Base
31
+ normalizes :customers, :allow_inserts => true
32
+ validates :email_address, :presence => true
33
+ validates_uniqueness_of :email_address
34
+ end
35
+
36
+ class Customer < ActiveRecord::Base
37
+ has_normalized :customer_status
38
+ has_normalized :email_address, :allow_inserts => true
39
+ end
40
+
41
+ ActiveRecord::Base.connection.execute("INSERT INTO customer_statuses (customer_status) VALUES ('new')")
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: normalize_it
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Dave Havlicek
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-22 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.0
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: jeweler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.5.2
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rcov
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ description: |-
61
+ Seamlessly deal with database tables with normalized info. Auto-generation of relationships
62
+ and delegators.
63
+ email: xens@comcast.net
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - LICENSE.txt
70
+ - README.rdoc
71
+ files:
72
+ - .document
73
+ - .rspec
74
+ - Gemfile
75
+ - Gemfile.lock
76
+ - LICENSE.txt
77
+ - README.rdoc
78
+ - Rakefile
79
+ - VERSION
80
+ - init.rb
81
+ - lib/normalize_it.rb
82
+ - normalize_it.gemspec
83
+ - spec/normalize_it_spec.rb
84
+ - spec/schema.rb
85
+ - spec/spec_helper.rb
86
+ has_rdoc: true
87
+ homepage: http://github.com/snex/normalize_it
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: -807153901
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: "0"
110
+ requirements: []
111
+
112
+ rubyforge_project:
113
+ rubygems_version: 1.6.0
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: normalize_it makes it easy to seamlessly manage database tables that have been normalized.
117
+ test_files:
118
+ - spec/normalize_it_spec.rb
119
+ - spec/schema.rb
120
+ - spec/spec_helper.rb