normalize_it 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +77 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +70 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/normalize_it.rb +168 -0
- data/normalize_it.gemspec +68 -0
- data/spec/normalize_it_spec.rb +94 -0
- data/spec/schema.rb +15 -0
- data/spec/spec_helper.rb +41 -0
- metadata +120 -0
data/.document
ADDED
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
|
data/Gemfile.lock
ADDED
@@ -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
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.rdoc
ADDED
@@ -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
|
+
|
data/Rakefile
ADDED
@@ -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'
|
data/lib/normalize_it.rb
ADDED
@@ -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
|
data/spec/schema.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|