constant_cache 0.0.1

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/HISTORY ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-04-08
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 [name of plugin creator]
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 ADDED
@@ -0,0 +1,75 @@
1
+ ConstantCache
2
+ ===============
3
+
4
+ When your database has tables that store lookup data, there is a tendency
5
+ to provide those values as constants in the model. If you have an
6
+ account_statuses table with a corresponding model, your constants may look
7
+ like this:
8
+
9
+ class AccountStatus
10
+ ACTIVE = 1
11
+ PENDING = 2
12
+ DISABLED = 3
13
+ end
14
+
15
+ There are a couple of problems with this approach:
16
+
17
+ As you add more lookup data to the table, you need to ensure that you're
18
+ updating your models along with the data.
19
+
20
+ The constants are stored as integer values and need to match up exactly
21
+ with the data that's in the table (not necessarily a bad thing), but this
22
+ solution forces you to write code like this:
23
+
24
+ Account.new(:username => 'preagan', :status => AccountStatus.find(AccountStatus::PENDING))
25
+
26
+ This requires multiple calls to find and obfuscates the code a bit. Since classes
27
+ in Ruby are executable code, we can cache the objects from the database at runtime
28
+ and use them in your application.
29
+
30
+ Installation
31
+ ============
32
+
33
+ This code is packaged as a gem, so simply use the `gem` command to install:
34
+
35
+ gem install constant_cache
36
+
37
+ Example
38
+ =======
39
+
40
+ "Out of the box," the constant_cache gem assumes that you want to use the 'name' column to generate
41
+ constants from a column called 'name' in your database table. Assuming this schema:
42
+
43
+ create_table :account_statuses do |t|
44
+ t.string :name, :description
45
+ end
46
+
47
+ AccountStatus.create!(:name => 'Active', :description => 'Active user account')
48
+ AccountStatus.create!(:name => 'Pending', :description => 'Pending user account')
49
+ AccountStatus.create!(:name => 'Disabled', :description => 'Disabled user account')
50
+
51
+ We can use the plugin to cache the data in the table:
52
+
53
+ class AccountStatus
54
+ caches_constants
55
+ end
56
+
57
+ Now you can write code that's a little cleaner and not use multiple unnecessary find calls:
58
+
59
+ Account.new(:username => 'preagan', :status => AccountStatus::PENDING)
60
+
61
+ If the column you want to use as the constant isn't 'name', you can set that in the model. If
62
+ we have :name, :slug, and :description, we can use 'slug' instead:
63
+
64
+ class AccountStatus
65
+ caches_constants :key => :slug
66
+ end
67
+
68
+ The value for the constant is truncated at 64 characters by default, but you can adjust this as
69
+ well:
70
+
71
+ class AccountStatus
72
+ caches_constants :limit => 16
73
+ end
74
+
75
+ Copyright (c) 2007 Patrick Reagan of Viget Labs (patrick.reagan@viget.com), released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+
5
+ require 'lib/constant_cache/version'
6
+
7
+ GEM = "constant_cache"
8
+ AUTHOR = "Patrick Reagan"
9
+ EMAIL = "patrick.reagan@viget.com"
10
+ HOMEPAGE = "http://www.viget.com/extend/"
11
+ SUMMARY = "Patches active record to add a caches_constants class method that will cache lookup data for your application."
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = GEM
15
+ s.version = ConstantCache::VERSION::STRING
16
+ s.platform = Gem::Platform::RUBY
17
+ s.has_rdoc = true
18
+ s.extra_rdoc_files = %w(README MIT-LICENSE HISTORY)
19
+ s.summary = SUMMARY
20
+ s.description = s.summary
21
+ s.author = AUTHOR
22
+ s.email = EMAIL
23
+ s.homepage = HOMEPAGE
24
+
25
+ s.add_dependency('activerecord', '>= 2.0.2')
26
+ s.add_dependency('activesupport', '>= 2.0.2')
27
+
28
+ s.require_path = 'lib'
29
+ s.autorequire = GEM
30
+ s.files = %w(MIT-LICENSE README Rakefile HISTORY) + Dir.glob("{lib,spec}/**/*")
31
+ end
32
+
33
+ Rake::GemPackageTask.new(spec) do |pkg|
34
+ pkg.gem_spec = spec
35
+ end
36
+
37
+ task :install => [:package] do
38
+ sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
39
+ end
40
+
41
+ Spec::Rake::SpecTask.new do |t|
42
+ t.spec_files = FileList['spec/examples/**/*_spec.rb']
43
+ t.rcov = true
44
+ t.rcov_opts = ['--exclude', 'spec']
45
+ t.spec_opts = ['--format', 'specdoc']
46
+ end
@@ -0,0 +1,6 @@
1
+ require 'constant_cache/format'
2
+ require 'constant_cache/cache_methods'
3
+
4
+ String.send(:include, ConstantCache::Format)
5
+ ActiveRecord::Base.send(:extend, ConstantCache::CacheMethods::ClassMethods)
6
+ ActiveRecord::Base.send(:include, ConstantCache::CacheMethods::InstanceMethods)
@@ -0,0 +1,44 @@
1
+ module ConstantCache
2
+
3
+ class DuplicateConstantError < StandardError; end
4
+ class InvalidLimitError < StandardError; end
5
+
6
+ module CacheMethods
7
+
8
+ module ClassMethods
9
+ def caches_constants(additional_options = {})
10
+ cattr_accessor :cache_options
11
+
12
+ self.cache_options = {:key => :name, :limit => 64}.merge(additional_options)
13
+
14
+ raise ConstantCache::InvalidLimitError, "Limit of #{self.cache_options[:limit]} is invalid" if self.cache_options[:limit] < 1
15
+ find(:all).each {|model| model.set_instance_as_constant }
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+
21
+ def constant_name
22
+ constant_name = self.send(self.class.cache_options[:key].to_sym).constant_name
23
+ constant_name = constant_name[0, self.class.cache_options[:limit]] unless constant_name.blank?
24
+ constant_name
25
+ end
26
+
27
+ def set_instance_as_constant
28
+ const = constant_name
29
+ if !const.blank?
30
+ raise ConstantCache::DuplicateConstantError, "Constant #{self.class.to_s}::#{const} has already been defined" if self.class.const_defined?(const)
31
+ self.class.const_set(const, self) if !const.blank?
32
+ end
33
+ end
34
+
35
+ # TODO: do we want this behavior?
36
+ # def constant_cache_destroy
37
+ # constant_name = constant_name()
38
+ # self.class.send(:remove_const, constant_name) if self.class.const_defined?(constant_name)
39
+ # end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ module ConstantCache
2
+ module Format
3
+ def constant_name
4
+ value = self.strip.gsub(/\s+/, '_').gsub(/[^\w_]/, '').gsub(/_{2,}/, '_').upcase
5
+ value = nil if value.blank?
6
+ value
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module ConstantCache
2
+
3
+ module VERSION
4
+ MAJOR = 0
5
+ MINOR = 0
6
+ TINY = 1
7
+
8
+ STRING = [MAJOR, MINOR, TINY].join('.')
9
+ end
10
+
11
+ end
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe SimpleClass, "with constant_cache mix-in" do
4
+
5
+ before { @value = 'pony' }
6
+
7
+ it "should set some default options" do
8
+ enable_caching(SimpleClass)
9
+ SimpleClass.cache_options.should == {:key => :name, :limit => 64}
10
+ end
11
+
12
+ it "should overwrite default options when passed new ones" do
13
+ additional_options = {:key => :new_key, :limit => 100}
14
+
15
+ enable_caching(SimpleClass, [], additional_options)
16
+ SimpleClass.cache_options.should == additional_options
17
+ end
18
+
19
+ it "should use name as the default constant key" do
20
+ enable_caching(SimpleClass, [{:name => 'one', :value => @value}])
21
+ SimpleClass::ONE.value.should == @value
22
+ end
23
+
24
+ it "should not set constant when value is nil" do
25
+ proc = lambda { enable_caching(SimpleClass, [{:name => '?', :value => @pony}]) }
26
+ proc.should_not change(SimpleClass.constants, :size)
27
+ end
28
+
29
+ it "should raise an exception with message when encountering a duplicate constant" do
30
+ proc = lambda { enable_caching(SimpleClass, [{:name => 'duplicate'}, {:name => 'duplicate'}]) }
31
+ proc.should raise_error(ConstantCache::DuplicateConstantError, 'Constant SimpleClass::DUPLICATE has already been defined')
32
+ end
33
+
34
+ it "should truncate long constant names" do
35
+ enable_caching(SimpleClass, [{:name => 'a' * 65, :value => @value}])
36
+ SimpleClass.const_get('A' * 64).value.should == @value
37
+ end
38
+
39
+ it "should honor configuration of truncation point" do
40
+ enable_caching(SimpleClass, [{:name => 'abcdef', :value => @value}], {:limit => 3})
41
+ SimpleClass::ABC.value.should == @value
42
+ end
43
+
44
+ it "should raise an exception when truncated constant names collide" do
45
+ proc = lambda { enable_caching(SimpleClass, [{:name => 'abcd'}, {:name => 'abef'}], {:limit => 2}) }
46
+ proc.should raise_error(ConstantCache::DuplicateConstantError)
47
+ end
48
+
49
+ it "should raise an exception when an invalid limit is set" do
50
+ limit = 0
51
+ proc = lambda { SimpleClass.caches_constants(:limit => limit) }
52
+ proc.should raise_error(ConstantCache::InvalidLimitError, "Limit of #{limit} is invalid")
53
+ end
54
+ end
55
+
56
+ describe AlternateClass, "with constant_cache mix-in" do
57
+
58
+ it "should allow override of key" do
59
+ enable_caching(AlternateClass, [{:name2 => 'foo bar', :value => @value}], {:key => :name2})
60
+ AlternateClass::FOO_BAR.value.should == @value
61
+ end
62
+
63
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe String, "with constant_name method" do
4
+
5
+ before { String.send(:include, ConstantCache::Format) }
6
+
7
+ it "should upcase its characters" do
8
+ 'test'.constant_name.should == 'TEST'
9
+ end
10
+
11
+ it "should replace whitespace with a single underscore" do
12
+ "test this \tformat\nplease.".constant_name.should == 'TEST_THIS_FORMAT_PLEASE'
13
+ end
14
+
15
+ it "should remove leading and trailing whitespace" do
16
+ ' test '.constant_name.should == 'TEST'
17
+ end
18
+
19
+ it "should remove non-word characters" do
20
+ '!test?'.constant_name.should == 'TEST'
21
+ end
22
+
23
+ it "should not singularize plural name" do
24
+ 'tests'.constant_name.should == 'TESTS'
25
+ end
26
+
27
+ it "should return nil when all characters are removed" do
28
+ '?'.constant_name.should be_nil
29
+ end
30
+
31
+ it "should collapse multiple underscores" do
32
+ 'test__me'.constant_name.should == 'TEST_ME'
33
+ end
34
+
35
+ end
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'mocha'
4
+
5
+ require 'activesupport'
6
+ require 'activerecord'
7
+
8
+
9
+
10
+ lib_dir = File.join(File.dirname(__FILE__), %w(.. lib constant_cache))
11
+
12
+ Dir.glob("#{lib_dir}/*.rb").each {|file| require file }
13
+
14
+ module ConstantCache
15
+ module SpecHelper
16
+
17
+ def enable_caching(klass, values = [], additional_options = {})
18
+ return_values = values.empty? ? [] : values.map {|params| klass.new(params) }
19
+
20
+ klass.expects(:find).with(:all).returns(return_values)
21
+ klass.caches_constants(additional_options)
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ Spec::Runner.configuration.mock_with :mocha
28
+ Spec::Runner.configuration.include ConstantCache::SpecHelper
29
+
30
+ String.send(:include, ConstantCache::Format)
31
+ ActiveRecord::Base.send(:include, ConstantCache::CacheMethods::InstanceMethods)
32
+ ActiveRecord::Base.send(:extend, ConstantCache::CacheMethods::ClassMethods)
33
+
34
+ class BaseClass < ActiveRecord::Base
35
+ def self.columns; []; end
36
+ end
37
+
38
+ class SimpleClass < BaseClass
39
+ PREDEFINED = 'foo'
40
+ attr_accessor :name, :value
41
+ end
42
+
43
+ class AlternateClass < BaseClass;
44
+ attr_accessor :name2, :value
45
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: constant_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Patrick Reagan
8
+ autorequire: constant_cache
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-04-08 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.2
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: activesupport
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 2.0.2
32
+ version:
33
+ description: Patches active record to add a caches_constants class method that will cache lookup data for your application.
34
+ email: patrick.reagan@viget.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README
41
+ - MIT-LICENSE
42
+ - HISTORY
43
+ files:
44
+ - MIT-LICENSE
45
+ - README
46
+ - Rakefile
47
+ - HISTORY
48
+ - lib/constant_cache
49
+ - lib/constant_cache/cache_methods.rb
50
+ - lib/constant_cache/format.rb
51
+ - lib/constant_cache/version.rb
52
+ - lib/constant_cache.rb
53
+ - spec/examples
54
+ - spec/examples/constant_cache
55
+ - spec/examples/constant_cache/cache_methods_spec.rb
56
+ - spec/examples/constant_cache/format_spec.rb
57
+ - spec/spec_helper.rb
58
+ has_rdoc: true
59
+ homepage: http://www.viget.com/extend/
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.0.0
81
+ signing_key:
82
+ specification_version: 2
83
+ summary: Patches active record to add a caches_constants class method that will cache lookup data for your application.
84
+ test_files: []
85
+