arid_cache 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -8,6 +8,7 @@ AridCache simplifies caching by supporting auto-expiring cache keys - as well as
8
8
 
9
9
  == Changes
10
10
 
11
+ v1.2.0: Fix Rails 3 ActiveRecord hooks & remove some Rails dependencies
11
12
  v1.0.5: Support <tt>:raw</tt> and <tt>:clear</tt> options.
12
13
 
13
14
  == Install
@@ -22,10 +23,6 @@ Then
22
23
 
23
24
  bundle install
24
25
 
25
- For some reason AridCache is not being included into ActiveRecord, so add the following to an initializer to get around that until I fix it:
26
-
27
- AridCache.init_rails
28
-
29
26
  <b>Rails 2:</b>
30
27
 
31
28
  Add the gem to your <tt>config/environment.rb</tt> file:
@@ -353,16 +350,12 @@ In some circumstances - like when you are querying on a named scope - if you hav
353
350
  == Compatibility
354
351
 
355
352
  Tested on Ruby 1.8.6, 1.8.7, REE 1.8.7 and 1.9.1.
356
- Tested in Rails 2.3.* and Rails 3
353
+ Tested in Rails 2.3.11, Rails 3.0.0, Rails 3.0.5
357
354
 
358
355
  For Ruby < 1.8.7 you probably want to include the following to extend the Array class with a <tt>count</tt> method. Otherwise your <tt>cached_<key>_count</tt> calls probably won't work:
359
356
 
360
357
  Array.class_eval { alias count size }
361
358
 
362
- For Rails 3 for some reason AridCache is not being included into ActiveRecord, so add the following to an initializer to get around that:
363
-
364
- AridCache.init_rails
365
-
366
359
  == Resources & Metrics
367
360
 
368
361
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.2.0
data/arid_cache.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{arid_cache}
8
- s.version = "1.1.1"
8
+ s.version = "1.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 = ["Karl Varga"]
12
- s.date = %q{2011-03-21}
12
+ s.date = %q{2011-03-28}
13
13
  s.description = %q{AridCache makes caching easy and effective. AridCache supports caching on all your model named scopes, class methods and instance methods right out of the box. AridCache prevents caching logic from cluttering your models and clarifies your logic by making explicit calls to cached result sets.
14
14
  AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
15
15
  }
@@ -31,6 +31,9 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
31
31
  "lib/arid_cache/active_record.rb",
32
32
  "lib/arid_cache/cache_proxy.rb",
33
33
  "lib/arid_cache/helpers.rb",
34
+ "lib/arid_cache/inflector.rb",
35
+ "lib/arid_cache/inflector/inflections.rb",
36
+ "lib/arid_cache/railtie.rb",
34
37
  "lib/arid_cache/store.rb",
35
38
  "rails/init.rb",
36
39
  "spec/arid_cache/active_record_spec.rb",
data/init.rb CHANGED
@@ -3,4 +3,4 @@ begin
3
3
  rescue LoadError
4
4
  require 'arid_cache' # From gem
5
5
  end
6
- AridCache.init_rails
6
+ AridCache.init_rails if defined?(Rails)
data/lib/arid_cache.rb CHANGED
@@ -5,6 +5,8 @@ require 'arid_cache/helpers'
5
5
  require 'arid_cache/store'
6
6
  require 'arid_cache/active_record'
7
7
  require 'arid_cache/cache_proxy'
8
+ require 'arid_cache/railtie' if defined?(Rails)
9
+ require 'arid_cache/inflector'
8
10
 
9
11
  module AridCache
10
12
  extend AridCache::Helpers
@@ -36,7 +36,7 @@ module AridCache
36
36
  elsif options[:auto_expire]
37
37
  self.cache_key
38
38
  else
39
- "#{self.class.name.downcase.pluralize}/#{self.id}"
39
+ "#{AridCache::Inflector.pluralize(self.class.name.downcase)}/#{self.id}"
40
40
  end
41
41
  'arid-cache-' + object_key + '-' + key.to_s
42
42
  end
@@ -39,7 +39,7 @@ module AridCache
39
39
  end
40
40
 
41
41
  def self.clear_instance_caches(object)
42
- key = (object.is_a?(Class) ? object : object.class).name.pluralize.downcase
42
+ key = AridCache::Inflector.pluralize((object.is_a?(Class) ? object : object.class).name).downcase
43
43
  Rails.cache.delete_matched(%r[arid-cache-#{key}.*])
44
44
  end
45
45
 
@@ -0,0 +1,58 @@
1
+ require 'arid_cache/inflector/inflections'
2
+
3
+ module AridCache
4
+ AridCache::Inflector.inflections do |inflect|
5
+ inflect.plural(/$/, 's')
6
+ inflect.plural(/s$/i, 's')
7
+ inflect.plural(/(ax|test)is$/i, '\1es')
8
+ inflect.plural(/(octop|vir)us$/i, '\1i')
9
+ inflect.plural(/(alias|status)$/i, '\1es')
10
+ inflect.plural(/(bu)s$/i, '\1ses')
11
+ inflect.plural(/(buffal|tomat)o$/i, '\1oes')
12
+ inflect.plural(/([ti])um$/i, '\1a')
13
+ inflect.plural(/sis$/i, 'ses')
14
+ inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
15
+ inflect.plural(/(hive)$/i, '\1s')
16
+ inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
17
+ inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
18
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
19
+ inflect.plural(/([m|l])ouse$/i, '\1ice')
20
+ inflect.plural(/^(ox)$/i, '\1en')
21
+ inflect.plural(/(quiz)$/i, '\1zes')
22
+
23
+ inflect.singular(/s$/i, '')
24
+ inflect.singular(/(n)ews$/i, '\1ews')
25
+ inflect.singular(/([ti])a$/i, '\1um')
26
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
27
+ inflect.singular(/(^analy)ses$/i, '\1sis')
28
+ inflect.singular(/([^f])ves$/i, '\1fe')
29
+ inflect.singular(/(hive)s$/i, '\1')
30
+ inflect.singular(/(tive)s$/i, '\1')
31
+ inflect.singular(/([lr])ves$/i, '\1f')
32
+ inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
33
+ inflect.singular(/(s)eries$/i, '\1eries')
34
+ inflect.singular(/(m)ovies$/i, '\1ovie')
35
+ inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
36
+ inflect.singular(/([m|l])ice$/i, '\1ouse')
37
+ inflect.singular(/(bus)es$/i, '\1')
38
+ inflect.singular(/(o)es$/i, '\1')
39
+ inflect.singular(/(shoe)s$/i, '\1')
40
+ inflect.singular(/(cris|ax|test)es$/i, '\1is')
41
+ inflect.singular(/(octop|vir)i$/i, '\1us')
42
+ inflect.singular(/(alias|status)es$/i, '\1')
43
+ inflect.singular(/^(ox)en/i, '\1')
44
+ inflect.singular(/(vert|ind)ices$/i, '\1ex')
45
+ inflect.singular(/(matr)ices$/i, '\1ix')
46
+ inflect.singular(/(quiz)zes$/i, '\1')
47
+ inflect.singular(/(database)s$/i, '\1')
48
+
49
+ inflect.irregular('person', 'people')
50
+ inflect.irregular('man', 'men')
51
+ inflect.irregular('child', 'children')
52
+ inflect.irregular('sex', 'sexes')
53
+ inflect.irregular('move', 'moves')
54
+ inflect.irregular('cow', 'kine')
55
+
56
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans))
57
+ end
58
+ end
@@ -0,0 +1,213 @@
1
+ module AridCache
2
+ module Inflector
3
+ extend self
4
+
5
+ # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
6
+ # inflection rules. Examples:
7
+ #
8
+ # AridCache::Inflector.inflections do |inflect|
9
+ # inflect.plural /^(ox)$/i, '\1\2en'
10
+ # inflect.singular /^(ox)en/i, '\1'
11
+ #
12
+ # inflect.irregular 'octopus', 'octopi'
13
+ #
14
+ # inflect.uncountable "equipment"
15
+ # end
16
+ #
17
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
18
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
19
+ # already have been loaded.
20
+ class Inflections
21
+ def self.instance
22
+ @__instance__ ||= new
23
+ end
24
+
25
+ attr_reader :plurals, :singulars, :uncountables, :humans
26
+
27
+ def initialize
28
+ @plurals, @singulars, @uncountables, @humans = [], [], [], []
29
+ end
30
+
31
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
32
+ # The replacement should always be a string that may include references to the matched data from the rule.
33
+ def plural(rule, replacement)
34
+ @uncountables.delete(rule) if rule.is_a?(String)
35
+ @uncountables.delete(replacement)
36
+ @plurals.insert(0, [rule, replacement])
37
+ end
38
+
39
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
40
+ # The replacement should always be a string that may include references to the matched data from the rule.
41
+ def singular(rule, replacement)
42
+ @uncountables.delete(rule) if rule.is_a?(String)
43
+ @uncountables.delete(replacement)
44
+ @singulars.insert(0, [rule, replacement])
45
+ end
46
+
47
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
48
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
49
+ #
50
+ # Examples:
51
+ # irregular 'octopus', 'octopi'
52
+ # irregular 'person', 'people'
53
+ def irregular(singular, plural)
54
+ @uncountables.delete(singular)
55
+ @uncountables.delete(plural)
56
+ if singular[0,1].upcase == plural[0,1].upcase
57
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
58
+ plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
59
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
60
+ else
61
+ plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
62
+ plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
63
+ plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
64
+ plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
65
+ singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
66
+ singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
67
+ end
68
+ end
69
+
70
+ # Add uncountable words that shouldn't be attempted inflected.
71
+ #
72
+ # Examples:
73
+ # uncountable "money"
74
+ # uncountable "money", "information"
75
+ # uncountable %w( money information rice )
76
+ def uncountable(*words)
77
+ (@uncountables << words).flatten!
78
+ end
79
+
80
+ # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
81
+ # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
82
+ # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
83
+ #
84
+ # Examples:
85
+ # human /_cnt$/i, '\1_count'
86
+ # human "legacy_col_person_name", "Name"
87
+ def human(rule, replacement)
88
+ @humans.insert(0, [rule, replacement])
89
+ end
90
+
91
+ # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
92
+ # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
93
+ # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
94
+ #
95
+ # Examples:
96
+ # clear :all
97
+ # clear :plurals
98
+ def clear(scope = :all)
99
+ case scope
100
+ when :all
101
+ @plurals, @singulars, @uncountables = [], [], []
102
+ else
103
+ instance_variable_set "@#{scope}", []
104
+ end
105
+ end
106
+ end
107
+
108
+ # Yields a singleton instance of Inflector::Inflections so you can specify additional
109
+ # inflector rules.
110
+ #
111
+ # Example:
112
+ # AridCache::Inflector.inflections do |inflect|
113
+ # inflect.uncountable "rails"
114
+ # end
115
+ def inflections
116
+ if block_given?
117
+ yield Inflections.instance
118
+ else
119
+ Inflections.instance
120
+ end
121
+ end
122
+
123
+ # Returns the plural form of the word in the string.
124
+ #
125
+ # Examples:
126
+ # "post".pluralize # => "posts"
127
+ # "octopus".pluralize # => "octopi"
128
+ # "sheep".pluralize # => "sheep"
129
+ # "words".pluralize # => "words"
130
+ # "CamelOctopus".pluralize # => "CamelOctopi"
131
+ def pluralize(word)
132
+ result = word.to_s.dup
133
+
134
+ if word.empty? || inflections.uncountables.include?(result.downcase)
135
+ result
136
+ else
137
+ inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
138
+ result
139
+ end
140
+ end
141
+
142
+ # The reverse of +pluralize+, returns the singular form of a word in a string.
143
+ #
144
+ # Examples:
145
+ # "posts".singularize # => "post"
146
+ # "octopi".singularize # => "octopus"
147
+ # "sheep".singularize # => "sheep"
148
+ # "word".singularize # => "word"
149
+ # "CamelOctopi".singularize # => "CamelOctopus"
150
+ def singularize(word)
151
+ result = word.to_s.dup
152
+
153
+ if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i }
154
+ result
155
+ else
156
+ inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
157
+ result
158
+ end
159
+ end
160
+
161
+ # Capitalizes the first word and turns underscores into spaces and strips a
162
+ # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
163
+ #
164
+ # Examples:
165
+ # "employee_salary" # => "Employee salary"
166
+ # "author_id" # => "Author"
167
+ def humanize(lower_case_and_underscored_word)
168
+ result = lower_case_and_underscored_word.to_s.dup
169
+
170
+ inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
171
+ result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
172
+ end
173
+
174
+ # Capitalizes all the words and replaces some characters in the string to create
175
+ # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
176
+ # used in the Rails internals.
177
+ #
178
+ # +titleize+ is also aliased as as +titlecase+.
179
+ #
180
+ # Examples:
181
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
182
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
183
+ def titleize(word)
184
+ humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
185
+ end
186
+
187
+ # Create the name of a table like Rails does for models to table names. This method
188
+ # uses the +pluralize+ method on the last word in the string.
189
+ #
190
+ # Examples
191
+ # "RawScaledScorer".tableize # => "raw_scaled_scorers"
192
+ # "egg_and_ham".tableize # => "egg_and_hams"
193
+ # "fancyCategory".tableize # => "fancy_categories"
194
+ def tableize(class_name)
195
+ pluralize(underscore(class_name))
196
+ end
197
+
198
+ # Create a class name from a plural table name like Rails does for table names to models.
199
+ # Note that this returns a string and not a Class. (To convert to an actual class
200
+ # follow +classify+ with +constantize+.)
201
+ #
202
+ # Examples:
203
+ # "egg_and_hams".classify # => "EggAndHam"
204
+ # "posts".classify # => "Post"
205
+ #
206
+ # Singular names are not handled correctly:
207
+ # "business".classify # => "Busines"
208
+ def classify(table_name)
209
+ # strip out any leading schema name
210
+ camelize(singularize(table_name.to_s.sub(/.*\./, '')))
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,14 @@
1
+ # Rails 3 configuration via Railtie
2
+
3
+ if defined?(Rails::Railtie)
4
+ module AridCache
5
+ class Railtie < Rails::Railtie
6
+
7
+ initializer 'arid_cache.init' do
8
+ ActiveSupport.on_load(:active_record) do
9
+ include AridCache::ActiveRecord
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,6 +1,6 @@
1
1
  module AridCache
2
2
  class Store < Hash
3
-
3
+
4
4
  # AridCache::Store::Blueprint
5
5
  #
6
6
  # Stores options and blocks that are used to generate results for finds
@@ -13,40 +13,40 @@ module AridCache
13
13
  self.proc = proc
14
14
  self.opts = opts
15
15
  end
16
-
16
+
17
17
  def klass=(object) # store the class name only
18
18
  self['klass'] = object.is_a?(Class) ? object.name : object.class.name
19
19
  end
20
-
20
+
21
21
  def klass
22
22
  self['klass'].constantize unless self['klass'].nil?
23
- end
24
-
23
+ end
24
+
25
25
  def opts=(value)
26
26
  self['opts'] = value.symbolize_keys! unless !value.respond_to?(:symbolize_keys)
27
- end
28
-
27
+ end
28
+
29
29
  def opts
30
30
  self['opts'] || {}
31
31
  end
32
-
32
+
33
33
  def proc(object=nil)
34
34
  if self['proc'].nil? && !object.nil?
35
35
  self['proc'] = key
36
36
  else
37
37
  self['proc']
38
38
  end
39
- end
39
+ end
40
40
  end
41
-
41
+
42
42
  #
43
43
  # Capture cache configurations and blocks and store them in the store.
44
44
  #
45
45
  # A block is evaluated within the scope of this class. The blocks
46
46
  # contains calls to methods which define the caches and give options
47
- # for each cache.
47
+ # for each cache.
48
48
  #
49
- # Don't instantiate directly. Rather instantiate the Instance- or
49
+ # Don't instantiate directly. Rather instantiate the Instance- or
50
50
  # ClassCacheConfiguration.
51
51
  Configuration = Struct.new(:klass, :global_opts) do
52
52
 
@@ -54,7 +54,7 @@ module AridCache
54
54
  self.global_opts = global_opts
55
55
  self.klass = klass
56
56
  end
57
-
57
+
58
58
  def method_missing(key, *args, &block)
59
59
  opts = global_opts.merge(args.empty? ? {} : args.first)
60
60
  case self
@@ -67,15 +67,15 @@ module AridCache
67
67
  end
68
68
  class InstanceCacheConfiguration < Configuration; end #:nodoc:
69
69
  class ClassCacheConfiguration < Configuration; end #:nodoc:
70
-
70
+
71
71
  def has?(object, key)
72
72
  self.include?(object_store_key(object, key))
73
73
  end
74
-
74
+
75
75
  # Empty the proc store
76
76
  def delete!
77
77
  delete_if { true }
78
- end
78
+ end
79
79
 
80
80
  def self.instance
81
81
  @@singleton_instance ||= self.new
@@ -84,7 +84,7 @@ module AridCache
84
84
  def find(object, key)
85
85
  self[object_store_key(object, key)]
86
86
  end
87
-
87
+
88
88
  def add_instance_cache_configuration(klass, key, opts, proc)
89
89
  add_generic_cache_configuration(instance_store_key(klass, key), klass, key, opts, proc)
90
90
  end
@@ -96,7 +96,7 @@ module AridCache
96
96
  def add_object_cache_configuration(object, key, opts, proc)
97
97
  add_generic_cache_configuration(object_store_key(object, key), object, key, opts, proc)
98
98
  end
99
-
99
+
100
100
  def find_or_create(object, key)
101
101
  store_key = object_store_key(object, key)
102
102
  if self.include?(store_key)
@@ -105,20 +105,20 @@ module AridCache
105
105
  self[store_key] = AridCache::Store::Blueprint.new(object, key)
106
106
  end
107
107
  end
108
-
108
+
109
109
  protected
110
-
110
+
111
111
  def add_generic_cache_configuration(store_key, *args)
112
112
  self[store_key] = AridCache::Store::Blueprint.new(*args)
113
113
  end
114
-
114
+
115
115
  def initialize
116
116
  end
117
-
117
+
118
118
  def class_store_key(klass, key); klass.name.downcase + '-' + key.to_s; end
119
- def instance_store_key(klass, key); klass.name.downcase.pluralize + '-' + key.to_s; end
119
+ def instance_store_key(klass, key); AridCache::Inflector.pluralize(klass.name.downcase) + '-' + key.to_s; end
120
120
  def object_store_key(object, key)
121
121
  case object; when Class; class_store_key(object, key); else; instance_store_key(object.class, key); end
122
122
  end
123
- end
123
+ end
124
124
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arid_cache
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 1
9
- - 1
10
- version: 1.1.1
8
+ - 2
9
+ - 0
10
+ version: 1.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Karl Varga
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-03-21 00:00:00 -07:00
18
+ date: 2011-03-28 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -99,6 +99,9 @@ files:
99
99
  - lib/arid_cache/active_record.rb
100
100
  - lib/arid_cache/cache_proxy.rb
101
101
  - lib/arid_cache/helpers.rb
102
+ - lib/arid_cache/inflector.rb
103
+ - lib/arid_cache/inflector/inflections.rb
104
+ - lib/arid_cache/railtie.rb
102
105
  - lib/arid_cache/store.rb
103
106
  - rails/init.rb
104
107
  - spec/arid_cache/active_record_spec.rb