Permalink
Browse files

initial import

  • Loading branch information...
0 parents commit 8df4a2f8c0fb9a44bfce37eef77c13771fc32fa9 @ryanb committed Jul 11, 2008
Showing with 220 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. 0 CHANGELOG
  3. +20 −0 LICENSE
  4. +113 −0 README
  5. +21 −0 Rakefile
  6. +1 −0 lib/association_freezer.rb
  7. +8 −0 script/console
  8. +14 −0 script/destroy
  9. +14 −0 script/generate
  10. +15 −0 spec/spec_helper.rb
  11. +2 −0 tasks/deployment.rake
  12. +9 −0 tasks/spec.rake
@@ -0,0 +1,3 @@
+pkg
+doc
+*.sqlite3
No changes.
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Ryan Bates
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
113 README
@@ -0,0 +1,113 @@
+= Association Freezer
+
+Freeze a belongs_to association to ignore any future changes.
+
+NOTE: This gem is in very early development, and the documentation
+below is not yet implemented.
+
+
+== Install
+
+First specify it in your Rails config.
+
+ config.gem 'ryanb-association-freezer', :lib => 'association_freezer', :source => 'http://gems.github.com'
+
+And then install it.
+
+ rake gems:install
+
+Follow the instructions below to finish setup. Rails 2.1 or later
+required.
+
+
+== Scenario
+
+Let's say we have an Order model with a couple belongs_to associations
+for setting the billing and shipping address.
+
+ class Order < ActiveRecord::Base
+ belongs_to :shipping_address, :class_name => 'Address'
+ belongs_to :billing_address, :class_name => 'Address'
+ # ...
+ end
+
+Consider this scenario. A user comes to the site and places an order.
+Then he comes back a month later and changes his address. Now our Order
+model is incorrect, it will point to this new address instead of the
+old one he placed the order with.
+
+There's a couple ways to solve this, one is to create a new Address
+model whenever one edits the address. Another is to freeze the address
+attributes upon placing an order so it isn't effected when the original
+Address changes. This gem will help with the latter.
+
+
+== Usage
+
+First you need to generate a binary (or blob) column to hold each
+frozen assoiation. The name of the column must start with "frozen_" and
+end with the association name. You can do that with a migration like
+this:
+
+ script/generate migration add_frozen_addresses_to_orders \
+ frozen_billing_address:binary frozen_shipping_address:binary
+
+
+Next, enable the association freezer on your model. This must be done
+AFTER the associations have been specified.
+
+ class Order < ActiveRecord::Base
+ belongs_to :shipping_address, :class_name => 'Address'
+ belongs_to :billing_address, :class_name => 'Address'
+ enable_association_freezer
+ #...
+ end
+
+Now you can freeze the association at will:
+
+ # in order
+ def purchase
+ #...
+ freeze_association :billing_address
+ freeze_association :shipping_address
+ save!
+ end
+
+When freezing an association, the attributes will be set to the frozen
+column. However, the model is not saved automatically, so you must call
+save at some point after freezing.
+
+Here's where the magic starts. Now when you fetch the frozen
+association (order.billing_address), the association freezer will step
+in and return an Address model which is built from the attributes in
+the frozen column, not the attributes in the addresses table.
+Therefore, any changes to the address will not effect the order's
+address. "freeze" is called on the model before returning it to prevent
+accidentally altering the data.
+
+You can also unfreeze an already frozen association if you want it to
+use the database record instead.
+
+ unfreeze_association :billing_address
+
+You can then freeze it again to use the updated attributes.
+
+
+== Alternative Usage
+
+Not only is this gem good at handling the situation above, but it can
+also be used as a caching technique. If the belongs_to association is
+frequently accessed, it can be more efficient to freeze (cache) it to
+remove the need for a database call. Of course with this approach you
+want it to stay in sync with the database, so you'll need to expire the
+cache (refreeze the association) whenever the associated model changes.
+
+
+== Development
+
+This project can be found on github at the following URL.
+
+http://github.com/ryanb/association-freezer/
+
+If you would like to contribute to this project, please fork the
+repository and send me a pull request.
@@ -0,0 +1,21 @@
+require 'rubygems'
+require 'rake'
+
+begin
+ require 'echoe'
+
+ Echoe.new('association-freezer', '0.1.0') do |p|
+ p.summary = "Freeze a belongs_to association."
+ p.description = "Freeze a belongs_to association."
+ p.url = "http://github.com/ryanb/association-freezer"
+ p.author = 'Ryan Bates'
+ p.email = "ryan (at) railscasts (dot) com"
+ p.ignore_pattern = ["script/*"]
+ end
+
+rescue LoadError => boom
+ puts "You are missing a dependency required for meta-operations on this gem."
+ puts "#{boom.to_s.capitalize}."
+end
+
+Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1 @@
+$:.unshift(File.dirname(__FILE__))
@@ -0,0 +1,8 @@
+#!/usr/bin/env ruby
+# File: script/console
+irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
+
+# loading the spec_helper file even though this isn't necessarily for specs
+# it's just a convenient, quick way to load all the requirements.
+puts "Loading association-freezer gem"
+exec "#{irb} -r irb/completion -r #{File.dirname(__FILE__) + '/../spec/spec_helper.rb'} --simple-prompt"
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/destroy'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
+RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/generate'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
+RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,15 @@
+require 'rubygems'
+require 'spec'
+require 'active_support'
+require 'active_record'
+require File.dirname(__FILE__) + '/../lib/association_freezer.rb'
+
+# setup database adapter
+ActiveRecord::Base.establish_connection({
+ :adapter => "sqlite3",
+ :dbfile => File.dirname(__FILE__) + "/test.sqlite3"
+})
+
+Spec::Runner.configure do |config|
+ config.mock_with :mocha
+end
@@ -0,0 +1,2 @@
+desc "Build the manifest and gemspec files."
+task :build => [:build_manifest, :build_gemspec]
@@ -0,0 +1,9 @@
+require 'spec/rake/spectask'
+
+spec_files = Rake::FileList["spec/**/*_spec.rb"]
+
+desc "Run specs"
+Spec::Rake::SpecTask.new do |t|
+ t.spec_files = spec_files
+ t.spec_opts = ["-c"]
+end

0 comments on commit 8df4a2f

Please sign in to comment.