denormalize-field 0.1.3 → 0.2.0

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/Gemfile CHANGED
@@ -1,9 +1,10 @@
1
1
  source "http://rubygems.org"
2
2
  gem "activerecord", "~> 3.2.0"
3
3
 
4
- group :development do
4
+ group :development, :test do
5
+ gem 'em-synchrony'
5
6
  gem 'pry'
6
- gem 'sqlite3'
7
+ gem 'pg'
7
8
  gem "rspec", "~> 2.8.0"
8
9
  gem "rdoc", "~> 3.12"
9
10
  gem "jeweler", "~> 1.8.4"
data/Gemfile.lock CHANGED
@@ -16,6 +16,9 @@ GEM
16
16
  builder (3.0.3)
17
17
  coderay (1.0.7)
18
18
  diff-lcs (1.1.3)
19
+ em-synchrony (1.0.3)
20
+ eventmachine (>= 1.0.0.beta.1)
21
+ eventmachine (1.0.3)
19
22
  git (1.2.5)
20
23
  i18n (0.6.1)
21
24
  jeweler (1.8.4)
@@ -29,6 +32,7 @@ GEM
29
32
  mocha (0.10.4)
30
33
  metaclass (~> 0.0.1)
31
34
  multi_json (1.3.6)
35
+ pg (0.14.1)
32
36
  pry (0.9.10)
33
37
  coderay (~> 1.0.5)
34
38
  method_source (~> 0.8)
@@ -45,7 +49,6 @@ GEM
45
49
  diff-lcs (~> 1.1.2)
46
50
  rspec-mocks (2.8.0)
47
51
  slop (3.3.3)
48
- sqlite3 (1.3.6)
49
52
  tzinfo (0.3.33)
50
53
 
51
54
  PLATFORMS
@@ -53,9 +56,10 @@ PLATFORMS
53
56
 
54
57
  DEPENDENCIES
55
58
  activerecord (~> 3.2.0)
59
+ em-synchrony
56
60
  jeweler (~> 1.8.4)
57
61
  mocha
62
+ pg
58
63
  pry
59
64
  rdoc (~> 3.12)
60
65
  rspec (~> 2.8.0)
61
- sqlite3
data/README.rdoc CHANGED
@@ -1,15 +1,18 @@
1
1
  = denormalize-field
2
2
 
3
+ Postgres only as of 0.2
4
+
3
5
  Denormalizes fields in ActiveRecord models in order to avoid SQL joins and hydrating Ruby Objects to obtain a simple fields.
4
6
 
5
7
  = Usage
6
8
 
7
- class Post < ActiveRecord::Base
8
- belongs_to :category
9
- denormalizes :category => :name
10
- end
9
+ class Post < ActiveRecord::Base
10
+ belongs_to :category
11
+ denormalizes :category => :name
12
+ end
13
+
14
+ category = Category.create(:name => "News")
15
+ post = Post.create(:category => category)
11
16
 
12
- category = Category.create(:name => "News")
13
- post = Post.create(:category => category)
17
+ post.category_name # "News" (you have to create the migration to add Post#category_name manually)
14
18
 
15
- post.category_name # "News" (you have to create the migration to add Post#category_name manually)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
@@ -0,0 +1,7 @@
1
+ development:
2
+ adapter: postgresql
3
+ database: denormalizefielddev
4
+
5
+ test: &test
6
+ adapter: postgresql
7
+ database: denormalizefieldtest
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "denormalize-field"
8
- s.version = "0.1.3"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Len Smith"]
12
- s.date = "2012-10-15"
12
+ s.date = "2013-05-28"
13
13
  s.description = "Denormalize ActiveRecord fields for performance reasons"
14
14
  s.email = "len@barrison.com"
15
15
  s.extra_rdoc_files = [
@@ -25,8 +25,12 @@ Gem::Specification.new do |s|
25
25
  "README.rdoc",
26
26
  "Rakefile",
27
27
  "VERSION",
28
+ "config/database.yml",
28
29
  "denormalize-field.gemspec",
29
30
  "lib/denormalize-field.rb",
31
+ "lib/tasks.rb",
32
+ "lib/tasks/denormalize-tasks.rake",
33
+ "spec/db_connect.rb",
30
34
  "spec/denormalize-field_spec.rb",
31
35
  "spec/schema.rb",
32
36
  "spec/spec_helper.rb"
@@ -42,16 +46,18 @@ Gem::Specification.new do |s|
42
46
 
43
47
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
44
48
  s.add_runtime_dependency(%q<activerecord>, ["~> 3.2.0"])
49
+ s.add_development_dependency(%q<em-synchrony>, [">= 0"])
45
50
  s.add_development_dependency(%q<pry>, [">= 0"])
46
- s.add_development_dependency(%q<sqlite3>, [">= 0"])
51
+ s.add_development_dependency(%q<pg>, [">= 0"])
47
52
  s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
48
53
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
49
54
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
50
55
  s.add_development_dependency(%q<mocha>, [">= 0"])
51
56
  else
52
57
  s.add_dependency(%q<activerecord>, ["~> 3.2.0"])
58
+ s.add_dependency(%q<em-synchrony>, [">= 0"])
53
59
  s.add_dependency(%q<pry>, [">= 0"])
54
- s.add_dependency(%q<sqlite3>, [">= 0"])
60
+ s.add_dependency(%q<pg>, [">= 0"])
55
61
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
56
62
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
57
63
  s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
@@ -59,8 +65,9 @@ Gem::Specification.new do |s|
59
65
  end
60
66
  else
61
67
  s.add_dependency(%q<activerecord>, ["~> 3.2.0"])
68
+ s.add_dependency(%q<em-synchrony>, [">= 0"])
62
69
  s.add_dependency(%q<pry>, [">= 0"])
63
- s.add_dependency(%q<sqlite3>, [">= 0"])
70
+ s.add_dependency(%q<pg>, [">= 0"])
64
71
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
65
72
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
66
73
  s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
@@ -2,7 +2,18 @@ require 'active_record'
2
2
  require 'active_support'
3
3
  require 'active_support/inflector'
4
4
 
5
+ class DenormalizeUpdater
6
+ def self.sync_all
7
+ DenormalizeFields::UPDATE_STATEMENTS.each do |sql|
8
+ DenormalizeFields::CLASSES.first.connection.execute sql
9
+ end
10
+ end
11
+ end
12
+
5
13
  module DenormalizeFields
14
+ UPDATE_STATEMENTS = []
15
+ CLASSES = []
16
+
6
17
  def denormalizes(hash)
7
18
  hash.keys.each do |key|
8
19
  _field_name = hash[key]
@@ -16,13 +27,23 @@ module DenormalizeFields
16
27
  end
17
28
 
18
29
  _klass = key.to_s.camelize.constantize
30
+ update_sql = "UPDATE #{table_name} SET #{_denormalized_field_name} = c2.#{_field_name} FROM #{table_name} c1 INNER JOIN #{_klass.table_name} c2 on c2.id = c1.#{key}_id"
31
+
19
32
  _klass.after_save do
20
33
  if self.send "#{_field_name}_changed?"
21
- self.send(_original_klass.name.downcase.pluralize).each do |child|
22
- child.update_attribute _denormalized_field_name, self.send(_field_name)
23
- end
34
+ update_sql = "UPDATE #{_original_klass.table_name} SET #{_denormalized_field_name} = '#{self.send(_field_name)}' where #{key}_id = #{self.id}"
35
+ self.connection.execute update_sql
24
36
  end
25
37
  end
38
+
39
+ self.class.class_eval <<-EVAL
40
+ define_method "#{_klass.table_name}_out_of_sync" do
41
+ #{self.name}.where("id in (SELECT c1.id FROM #{table_name} c1 INNER JOIN #{_klass.table_name} c2 on c2.id = c1.#{key}_id where c1.#{_denormalized_field_name} != c2.#{_field_name})")
42
+ end
43
+ EVAL
44
+
45
+ UPDATE_STATEMENTS.push update_sql
46
+ CLASSES.push self
26
47
  end
27
48
  end
28
49
  end
data/lib/tasks.rb ADDED
@@ -0,0 +1,5 @@
1
+ class DenormalizeFieldTask < Rails::Railtie
2
+ rake_tasks do
3
+ Dir[File.join(File.dirname(__FILE__),'lib/tasks/*.rake')].each { |f| load f }
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ namespace :denormalize do
2
+ task :show_outdated do
3
+ DenormalizeFields::CLASSES.each do |klass|
4
+ p "Out of sync for #{klass.name}: #{klass.out_of_sync.count}"
5
+ end
6
+ end
7
+
8
+ task :sync do
9
+ DenormalizeUpdater.sync_all
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+
2
+ require 'erb'
3
+ require 'uri'
4
+ require 'em-synchrony/activerecord'
5
+
6
+ class DbConnect
7
+ attr_accessor :config
8
+ def initialize
9
+ @db = URI.parse(ENV['DATABASE_URL'] || 'http://localhost')
10
+ if @db.scheme == 'postgres' # This section makes Heroku work
11
+ ActiveRecord::Base.establish_connection(
12
+ :adapter => @db.scheme == 'postgres' ? 'postgresql' : @db.scheme,
13
+ :host => @db.host,
14
+ :username => @db.user,
15
+ :password => @db.password,
16
+ :database => @db.path[1..-1],
17
+ :encoding => 'utf8'
18
+ )
19
+ else # And this is for my local environment
20
+ environment = ENV['DATABASE_URL'] ? 'production' : 'development'
21
+ @db = YAML.load(ERB.new(File.read('config/database.yml')).result)[environment]
22
+ ActiveRecord::Base.establish_connection(@db)
23
+ @config = ActiveRecord::Base.connection.pool.spec.config
24
+ end
25
+ end
26
+ end
27
+
28
+ # connect to the database
29
+ DbConnect.new
@@ -9,6 +9,17 @@ class Post < ActiveRecord::Base
9
9
  denormalizes category: :name
10
10
  end
11
11
 
12
+ describe DenormalizeUpdater do
13
+ let(:category) { Category.new(name: "News") }
14
+ let(:post) { Post.create(category: category) }
15
+
16
+ it "syncs all records" do
17
+ post.connection.execute("UPDATE posts set category_name = 'cool story';")
18
+ in_sync_post = Post.create(category: category)
19
+ Post.categories_out_of_sync.should == [post]
20
+ end
21
+ end
22
+
12
23
  describe "DenormalizeField" do
13
24
  let(:category) { Category.new(name: "News") }
14
25
  let(:post) { Post.new(category: category) }
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
3
  require 'rspec'
4
4
  require 'denormalize-field'
5
+ require 'db_connect'
5
6
 
6
7
  # Requires supporting files with custom matchers and macros, etc,
7
8
  # in ./support/ and its subdirectories.
@@ -15,18 +16,21 @@ RSpec.configure do |config|
15
16
  end
16
17
  end
17
18
 
18
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
19
- :database => File.dirname(__FILE__) + "/denormalize-field.sqlite3")
19
+ ActiveRecord::Base.establish_connection adapter:'postgresql', database: 'denormalizefielddev'
20
+
21
+ begin
22
+ ActiveRecord::Base.connection.drop_table(:categories)
23
+ ActiveRecord::Base.connection.drop_table(:posts)
24
+ rescue
25
+ end
20
26
 
21
- ActiveRecord::Base.connection.drop_table(:categories)
22
- ActiveRecord::Base.connection.drop_table(:posts)
23
27
  ActiveRecord::Base.connection.create_table(:categories) do |t|
24
28
  t.string :name
25
29
  t.timestamps
26
30
  end
27
31
 
28
32
  ActiveRecord::Base.connection.create_table(:posts) do |t|
29
- t.string :category_id
33
+ t.integer :category_id
30
34
  t.string :category_name
31
35
  t.timestamps
32
36
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: denormalize-field
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-15 00:00:00.000000000 Z
12
+ date: 2013-05-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: 3.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: em-synchrony
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: pry
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -44,7 +60,7 @@ dependencies:
44
60
  - !ruby/object:Gem::Version
45
61
  version: '0'
46
62
  - !ruby/object:Gem::Dependency
47
- name: sqlite3
63
+ name: pg
48
64
  requirement: !ruby/object:Gem::Requirement
49
65
  none: false
50
66
  requirements:
@@ -139,8 +155,12 @@ files:
139
155
  - README.rdoc
140
156
  - Rakefile
141
157
  - VERSION
158
+ - config/database.yml
142
159
  - denormalize-field.gemspec
143
160
  - lib/denormalize-field.rb
161
+ - lib/tasks.rb
162
+ - lib/tasks/denormalize-tasks.rake
163
+ - spec/db_connect.rb
144
164
  - spec/denormalize-field_spec.rb
145
165
  - spec/schema.rb
146
166
  - spec/spec_helper.rb
@@ -159,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
179
  version: '0'
160
180
  segments:
161
181
  - 0
162
- hash: -2555535460486354925
182
+ hash: -308078005241704715
163
183
  required_rubygems_version: !ruby/object:Gem::Requirement
164
184
  none: false
165
185
  requirements: