cached_record 0.1.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.
@@ -0,0 +1,17 @@
1
+ development:
2
+ adapter: mysql2
3
+ host: localhost
4
+ database: cached_record_development
5
+ username: root
6
+ password:
7
+ pool: 5
8
+ timeout: 5000
9
+
10
+ test:
11
+ adapter: mysql2
12
+ host: localhost
13
+ database: cached_record_test
14
+ username: root
15
+ password:
16
+ pool: 5
17
+ timeout: 5000
@@ -0,0 +1,71 @@
1
+ DROP TABLE IF EXISTS `articles`;
2
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
3
+ /*!40101 SET character_set_client = utf8 */;
4
+ CREATE TABLE `articles` (
5
+ `id` int(11) NOT NULL AUTO_INCREMENT,
6
+ `title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
7
+ `content` text COLLATE utf8_unicode_ci,
8
+ `author_id` int(11) DEFAULT NULL,
9
+ `foo_id` int(11) DEFAULT NULL,
10
+ `published_at` datetime DEFAULT NULL,
11
+ `created_at` datetime NOT NULL,
12
+ `updated_at` datetime NOT NULL,
13
+ PRIMARY KEY (`id`),
14
+ KEY `author_id` (`author_id`)
15
+ ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
16
+ /*!40101 SET character_set_client = @saved_cs_client */;
17
+ INSERT INTO `articles` VALUES (1,'Behold! It\'s CachedRecord!','Cache ORM instances to avoid database queries',1,2,'2013-08-01 12:00:00','2013-08-01 10:00:00','2013-08-01 11:00:00');
18
+ DROP TABLE IF EXISTS `articles_tags`;
19
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
20
+ /*!40101 SET character_set_client = utf8 */;
21
+ CREATE TABLE `articles_tags` (
22
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
23
+ `article_id` int(11) DEFAULT NULL,
24
+ `tag_id` int(11) DEFAULT NULL,
25
+ PRIMARY KEY (`id`),
26
+ KEY `articles_tags` (`article_id`,`tag_id`)
27
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
28
+ /*!40101 SET character_set_client = @saved_cs_client */;
29
+ INSERT INTO `articles_tags` VALUES (1,1,1),(2,1,2);
30
+ DROP TABLE IF EXISTS `comments`;
31
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
32
+ /*!40101 SET character_set_client = utf8 */;
33
+ CREATE TABLE `comments` (
34
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
35
+ `content` text COLLATE utf8_unicode_ci,
36
+ `article_id` int(11) DEFAULT NULL,
37
+ `poster_id` int(11) DEFAULT NULL,
38
+ `created_at` datetime DEFAULT NULL,
39
+ `updated_at` datetime DEFAULT NULL,
40
+ PRIMARY KEY (`id`),
41
+ KEY `article_id` (`article_id`),
42
+ KEY `poster_id` (`poster_id`)
43
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
44
+ /*!40101 SET character_set_client = @saved_cs_client */;
45
+ INSERT INTO `comments` VALUES (1,'What a great article! :)',1,2,'2013-08-02 12:00:00','2013-08-02 13:00:00'),(2,'Thanks!',1,1,'2013-08-02 14:00:00','2013-08-02 14:00:00');
46
+ DROP TABLE IF EXISTS `tags`;
47
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
48
+ /*!40101 SET character_set_client = utf8 */;
49
+ CREATE TABLE `tags` (
50
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
51
+ `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
52
+ `created_at` datetime DEFAULT NULL,
53
+ `updated_at` datetime DEFAULT NULL,
54
+ PRIMARY KEY (`id`)
55
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
56
+ /*!40101 SET character_set_client = @saved_cs_client */;
57
+ INSERT INTO `tags` VALUES (1,'ruby','2013-08-01 08:00:00','2013-08-01 08:00:00'),(2,'gem','2013-08-01 08:01:00','2013-08-01 08:01:00');
58
+ DROP TABLE IF EXISTS `users`;
59
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
60
+ /*!40101 SET character_set_client = utf8 */;
61
+ CREATE TABLE `users` (
62
+ `id` int(11) NOT NULL AUTO_INCREMENT,
63
+ `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
64
+ `description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
65
+ `active` tinyint(1) DEFAULT NULL,
66
+ `created_at` datetime NOT NULL,
67
+ `updated_at` datetime NOT NULL,
68
+ PRIMARY KEY (`id`)
69
+ ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
70
+ /*!40101 SET character_set_client = @saved_cs_client */;
71
+ INSERT INTO `users` VALUES (1,'Paul Engel','Author of CachedRecord',1,'2013-08-01 11:00:00','2013-08-01 12:00:00'),(2,'Ken Adams','Some guy',1,'2013-08-02 10:00:00','2013-08-02 11:00:00');
@@ -0,0 +1,125 @@
1
+ require "dalli"
2
+ require "redis"
3
+
4
+ module CachedRecord
5
+ module Cache
6
+ class Error < StandardError; end
7
+
8
+ def self.setup(store, options = {})
9
+ if valid_store? store
10
+ send store, options
11
+ end
12
+ end
13
+
14
+ def self.memcached(options = nil)
15
+ if stores[:memcached].nil? || options
16
+ options ||= {}
17
+ host = options.delete(:host) || "localhost"
18
+ port = options.delete(:port) || 11211
19
+ stores[:memcached] = Dalli::Client.new "#{host}:#{port}", options
20
+ end
21
+ stores[:memcached]
22
+ end
23
+
24
+ def self.redis(options = nil)
25
+ if stores[:redis].nil? || options
26
+ options ||= {}
27
+ stores[:redis] = Redis.new options
28
+ end
29
+ stores[:redis]
30
+ end
31
+
32
+ def self.cache
33
+ @cache ||= {Dalli::Client => {}, Redis => {}}
34
+ end
35
+
36
+ def self.clear!
37
+ @cache = nil
38
+ end
39
+
40
+ def self.get(klass, id)
41
+ retained(klass, id) || begin
42
+ cache_string = store(klass).get(klass.cache_key(id)) || begin
43
+ return unless (instance = yield if block_given?)
44
+ set instance
45
+ end
46
+ json, epoch_time = split_cache_string(cache_string)
47
+ memoized(klass, id, epoch_time) do
48
+ klass.load_cache_json JSON.parse(json)
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.set(instance)
54
+ "#{instance.to_cache_json}#{"@#{Time.now.to_i}" if instance.class.as_cache[:memoize]}".tap do |cache_string|
55
+ store(instance.class).set instance.class.cache_key(instance.id), cache_string, instance.class.as_cache[:expire]
56
+ end
57
+ end
58
+
59
+ def self.expire(instance)
60
+ klass = instance.class
61
+ cache_key = klass.cache_key instance.id
62
+ store(klass).delete cache_key
63
+ cache[store(klass).class].delete cache_key if klass.as_cache[:memoize]
64
+ nil
65
+ end
66
+
67
+ def self.retained(klass, id)
68
+ return unless klass.as_cache[:memoize] && klass.as_cache[:retain]
69
+ cache_hash, cache_key = cache[store(klass).class], klass.cache_key(id)
70
+
71
+ if (cache_entry = cache_hash[cache_key]) && (Time.now.to_i < cache_entry[:cache_hit])
72
+ cache_entry[:instance]
73
+ end
74
+ end
75
+
76
+ def self.memoized(klass, id, epoch_time)
77
+ return yield unless klass.as_cache[:memoize]
78
+ cache_hash, cache_key = cache[store(klass).class], klass.cache_key(id)
79
+ cache_hit = klass.as_cache[:retain] ? {:cache_hit => (Time.now + klass.as_cache[:retain]).to_i} : {}
80
+
81
+ if (cache_entry = cache_hash[cache_key]) && (epoch_time == cache_entry[:epoch_time])
82
+ cache_entry.merge! cache_hit
83
+ cache_entry[:instance]
84
+ else
85
+ yield.tap do |instance|
86
+ cache_hash[cache_key] = {:instance => instance, :epoch_time => epoch_time}.merge(cache_hit) if instance
87
+ end
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def self.valid_store?(arg)
94
+ [:memcached, :redis].include?(arg.to_sym)
95
+ end
96
+
97
+ def self.stores
98
+ @stores ||= {}
99
+ end
100
+
101
+ def self.store(klass)
102
+ store = klass.as_cache[:store] || begin
103
+ if stores.size == 1
104
+ stores.keys.first
105
+ else
106
+ raise Error, "Cannot determine default cache store (store size is not 1: #{@stores.keys.sort.inspect})"
107
+ end
108
+ end
109
+ if valid_store?(store)
110
+ send(store)
111
+ else
112
+ raise Error, "Invalid cache store :#{store} passed"
113
+ end
114
+ end
115
+
116
+ def self.split_cache_string(string)
117
+ reg_exp = /@(\d+)$/
118
+ string.match(reg_exp)
119
+ json = string.gsub(reg_exp, "")
120
+ epoch_time = $1.to_i if $1
121
+ [json, epoch_time]
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,76 @@
1
+ module CachedRecord
2
+ module ORM
3
+ module ActiveRecord
4
+
5
+ def self.setup?
6
+ !!defined?(::ActiveRecord)
7
+ end
8
+
9
+ def self.setup
10
+ return false unless setup?
11
+ ::ActiveRecord::Base.send :include, ORM
12
+ ::ActiveRecord::Base.extend ClassMethods
13
+ ::ActiveRecord::Base.send :include, InstanceMethods
14
+ true
15
+ end
16
+
17
+ module ClassMethods
18
+ def uncached(id)
19
+ find(id)
20
+ end
21
+ def new_cached_instance(attributes, foreign_keys, variables)
22
+ super.tap do |instance|
23
+ instance.instance_variable_set :@new_record, false
24
+ end
25
+ end
26
+ private
27
+ def set_cached_association(instance, key, value)
28
+ return unless value
29
+ if reflection = _cache_reflection_(instance, key, value)
30
+ value = _cache_value_(instance, reflection, value)
31
+ instance.send :"#{reflection.name}=", value
32
+ end
33
+ end
34
+ def _cache_reflection_(instance, key, value)
35
+ if key.to_s.match /^_(.*)_ids?/
36
+ reflections[$1.pluralize.to_sym]
37
+ else
38
+ instance.send :"#{key}=", value
39
+ reflections.detect{|k, v| v.foreign_key == key.to_s}.try(:last)
40
+ end
41
+ end
42
+ def _cache_value_(instance, reflection, value)
43
+ if value.is_a? Array
44
+ (instance.respond_to?(:association) ? instance.association(reflection.name) : instance.send(reflection.name).instance_variable_get(:@association)).loaded!
45
+ value.collect{|x| reflection.klass.cached x}
46
+ else
47
+ reflection.klass.cached value
48
+ end
49
+ end
50
+ end
51
+
52
+ module InstanceMethods
53
+ def cache_attributes
54
+ as_json(cache_json_options.slice(:only).merge(:root => false)).symbolize_keys!.merge cache_foreign_keys
55
+ end
56
+ def cache_foreign_keys
57
+ (cache_json_options[:include] || {}).inject({}) do |json, name|
58
+ reflection = self.class.reflections[name]
59
+ json.merge cache_foreign_key(name, reflection, send(name))
60
+ end
61
+ end
62
+ def cache_foreign_key(name, reflection, value)
63
+ case reflection.macro
64
+ when :belongs_to
65
+ {:"#{reflection.foreign_key}" => value.try(:id)}
66
+ when :has_one
67
+ {:"_#{name.to_s.singularize}_id" => value.try(:id)}
68
+ when :has_many, :has_and_belongs_to_many
69
+ {:"_#{name.to_s.singularize}_ids" => value.collect(&:id)}
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,87 @@
1
+ module CachedRecord
2
+ module ORM
3
+ module DataMapper
4
+
5
+ def self.setup?
6
+ !!defined?(::DataMapper)
7
+ end
8
+
9
+ def self.setup
10
+ return false unless setup?
11
+ ::DataMapper::Resource.class_eval do
12
+ class << self
13
+ alias :included_without_cached_record :included
14
+ end
15
+ def self.included(base)
16
+ included_without_cached_record base
17
+ base.extend ORM::ClassMethods
18
+ base.extend ClassMethods
19
+ end
20
+ end
21
+ ::DataMapper::Resource.send :include, ORM::InstanceMethods
22
+ ::DataMapper::Resource.send :include, InstanceMethods
23
+ true
24
+ end
25
+
26
+ module ClassMethods
27
+ def uncached(id)
28
+ get(id)
29
+ end
30
+ def new_cached_instance(attributes, foreign_keys, variables)
31
+ super.tap do |instance|
32
+ instance.persistence_state = ::DataMapper::Resource::PersistenceState::Clean.new instance
33
+ end
34
+ end
35
+ private
36
+ def _new_cached_instance_(id, attributes)
37
+ new attributes.merge(:id => id)
38
+ end
39
+ def set_cached_association(instance, key, value)
40
+ return unless value
41
+ if relationship = _cache_relationship_(instance, key, value)
42
+ value = _cache_value_(instance, relationship, value)
43
+ instance.instance_variable_set relationship.instance_variable_name, value
44
+ end
45
+ end
46
+ def _cache_relationship_(instance, key, value)
47
+ if key.to_s.match /^_(.*)_ids?/
48
+ relationships[$1.pluralize.to_sym]
49
+ else
50
+ instance.send :"#{key}=", value
51
+ relationships.detect{|r| r.child_key.first.name.to_s == key.to_s}
52
+ end
53
+ end
54
+ def _cache_value_(instance, relationship, value)
55
+ if value.is_a? Array
56
+ value.collect{|x| relationship.child_model.cached x}
57
+ elsif relationship.is_a?(::DataMapper::Associations::ManyToOne::Relationship)
58
+ relationship.parent_model.cached value
59
+ end
60
+ end
61
+ end
62
+
63
+ module InstanceMethods
64
+ def cache_attributes
65
+ (cache_json_options[:only] ? as_json.slice(*cache_json_options[:only]) : as_json).symbolize_keys!.merge cache_foreign_keys
66
+ end
67
+ def cache_foreign_keys
68
+ (cache_json_options[:include] || {}).inject({}) do |json, name|
69
+ relationship = relationships[name]
70
+ json.merge cache_foreign_key(name, relationship, send(name))
71
+ end
72
+ end
73
+ def cache_foreign_key(name, relationship, value)
74
+ case relationship
75
+ when ::DataMapper::Associations::ManyToOne::Relationship
76
+ {:"#{relationship.child_key.first.name}" => value.try(:id)}
77
+ when ::DataMapper::Associations::OneToOne::Relationship
78
+ {:"_#{name.to_s.singularize}_id" => value.try(:id)}
79
+ when ::DataMapper::Associations::OneToMany::Relationship, ::DataMapper::Associations::ManyToMany::Relationship
80
+ {:"_#{name.to_s.singularize}_ids" => value.collect(&:id)}
81
+ end
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,188 @@
1
+ require "cached_record/orm/active_record"
2
+ require "cached_record/orm/data_mapper"
3
+
4
+ module CachedRecord
5
+ module ORM
6
+
7
+ def self.included(base)
8
+ base.extend ClassMethods
9
+ base.send :include, InstanceMethods
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def as_cache(*args)
15
+ @as_cache = parse_as_cache_options args if args.any?
16
+ @as_cache ||= {:as_json => {}}
17
+ end
18
+
19
+ def as_memoized_cache(*args)
20
+ retain = args.last.delete(:retain) if args.last.is_a?(Hash)
21
+ as_cache(*args).tap do |options|
22
+ options[:memoize] = true
23
+ options[:retain] = retain if retain
24
+ end
25
+ end
26
+
27
+ def cache_key(id)
28
+ "#{name.underscore.gsub("/", ".")}.#{id}"
29
+ end
30
+
31
+ def cache_root
32
+ "#{name.underscore.gsub(/^.*\//, "")}".to_sym
33
+ end
34
+
35
+ def cached(id)
36
+ Cache.get(self, id) do
37
+ uncached id
38
+ end
39
+ end
40
+
41
+ def uncached(id)
42
+ raise NotImplementedError, "Cannot fetch uncached `#{self.class}` instances"
43
+ end
44
+
45
+ def load_cache_json(json)
46
+ json.symbolize_keys!
47
+ properties, variables = cache_json_to_properties_and_variables(json)
48
+ foreign_keys, attributes = properties.partition{|k, v| k.to_s.match /_ids?$/}.collect{|x| Hash[x]}
49
+ new_cached_instance attributes, foreign_keys, variables
50
+ end
51
+
52
+ def new_cached_instance(attributes, foreign_keys, variables)
53
+ id = attributes.delete(:id) || attributes.delete("id")
54
+ _new_cached_instance_(id, attributes).tap do |instance|
55
+ instance.id = id if instance.respond_to?(:id=)
56
+ foreign_keys.each do |key, value|
57
+ set_cached_association instance, key, value
58
+ end
59
+ variables.each do |key, value|
60
+ instance.instance_variable_set key, value
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def _new_cached_instance_(id, attributes)
68
+ new attributes
69
+ end
70
+
71
+ def set_cached_association(instance, key, value)
72
+ raise NotImplementedError, "Cannot set cached association for `#{self}` instances"
73
+ end
74
+
75
+ def parse_as_cache_options(args)
76
+ if (symbol = args.first).is_a? Symbol
77
+ store = symbol
78
+ end
79
+ if (hash = args.last).is_a? Hash
80
+ expire = hash.delete :expire
81
+ as_json = parse_as_cache_json_options hash
82
+ end
83
+ {
84
+ :store => store,
85
+ :expire => expire,
86
+ :as_json => as_json || {}
87
+ }.reject{|key, value| value.nil?}
88
+ end
89
+
90
+ def parse_as_cache_json_options(options)
91
+ options.symbolize_keys!
92
+ validate_as_cache_json_options options
93
+ {}.tap do |opts|
94
+ opts[:only] = symbolize_array(options[:only]) if options[:only]
95
+ opts[:include] = symbolize_array(options[:include]) if options[:include]
96
+ opts[:memoize] = parse_memoize_options(options[:memoize]) if options[:memoize]
97
+ opts[:include_root] = true if options[:include_root]
98
+ end
99
+ end
100
+
101
+ def validate_as_cache_json_options(options)
102
+ options.assert_valid_keys :only, :include, :memoize, :include_root
103
+ options.slice(:only, :include).each do |key, value|
104
+ raise ArgumentError unless value.is_a?(Array)
105
+ end
106
+ if options[:memoize] && !options[:memoize].is_a?(Enumerable)
107
+ raise ArgumentError
108
+ end
109
+ if options.include?(:include_root) && ![true, false].include?(options[:include_root])
110
+ raise ArgumentError
111
+ end
112
+ end
113
+
114
+ def symbolize_array(array)
115
+ array.collect &:to_sym
116
+ end
117
+
118
+ def parse_memoize_options(options)
119
+ [options].flatten.inject({}) do |memo, x|
120
+ hash = x.is_a?(Hash) ? x : {x => :"@#{x}"}
121
+ memo.merge hash.inject({}){|h, (k, v)| h[k.to_sym] = v.to_sym; h}
122
+ end
123
+ end
124
+
125
+ def cache_json_to_properties_and_variables(json)
126
+ if as_cache[:as_json][:include_root]
127
+ properties = json.delete cache_root
128
+ variables = json.inject({}){|h, (k, v)| h[:"@#{k}"] = v; h}
129
+ [properties, variables]
130
+ else
131
+ json.partition{|k, v| !k.to_s.match(/^@/)}.collect{|x| Hash[x]}
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ module InstanceMethods
138
+
139
+ def cache_attributes
140
+ raise NotImplementedError, "Cannot return cache attributes for `#{self.class}` instances"
141
+ end
142
+
143
+ def cache_foreign_keys
144
+ raise NotImplementedError, "Cannot return cache foreign keys for `#{self.class}` instances"
145
+ end
146
+
147
+ def as_cache_json
148
+ attributes = {:id => id}.merge cache_attributes
149
+ variables = (cache_json_options[:memoize] || {}).inject({}) do |hash, (method, variable)|
150
+ hash[variable] = send method
151
+ hash
152
+ end
153
+ merge_cache_json attributes, variables
154
+ end
155
+
156
+ def merge_cache_json(attributes, variables)
157
+ if cache_json_options[:include_root]
158
+ variables = variables.inject({}){|h, (k, v)| h[k.to_s.gsub(/^@/, "").to_sym] = v; h}
159
+ {self.class.cache_root => attributes}.merge variables
160
+ else
161
+ attributes.merge variables
162
+ end
163
+ end
164
+
165
+ def to_cache_json
166
+ as_cache_json.to_json
167
+ end
168
+
169
+ def cache
170
+ self.class.cached id
171
+ true
172
+ end
173
+
174
+ def expire
175
+ Cache.expire self
176
+ true
177
+ end
178
+
179
+ private
180
+
181
+ def cache_json_options
182
+ self.class.as_cache[:as_json] || {}
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,7 @@
1
+ module CachedRecord
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ TINY = 0
5
+
6
+ VERSION = [MAJOR, MINOR, TINY].join(".")
7
+ end
@@ -0,0 +1,22 @@
1
+ require "gem_ext/redis"
2
+ require "cached_record/version"
3
+ require "cached_record/cache"
4
+ require "cached_record/orm"
5
+
6
+ module CachedRecord
7
+
8
+ def self.setup(*args)
9
+ args.each do |arg|
10
+ if arg.is_a?(Hash)
11
+ arg.each do |store, options|
12
+ Cache.setup store, options
13
+ end
14
+ else
15
+ Cache.setup arg
16
+ end
17
+ end
18
+ ORM::ActiveRecord.setup
19
+ ORM::DataMapper.setup
20
+ end
21
+
22
+ end
@@ -0,0 +1,25 @@
1
+ begin
2
+ require "redis"
3
+ rescue LoadError
4
+ end
5
+
6
+ class Redis
7
+
8
+ alias :set_without_cached_record :set
9
+ def set(key, value, ttl_or_options = nil)
10
+ if ttl_or_options.is_a? Integer
11
+ ttl = ttl_or_options
12
+ options = {}
13
+ else
14
+ options = ttl_or_options || {}
15
+ end
16
+ set_without_cached_record(key, value, options).tap do
17
+ expire key, ttl if ttl
18
+ end
19
+ end
20
+
21
+ def delete(*args)
22
+ del *args
23
+ end
24
+
25
+ end
data/log/.gitkeep ADDED
File without changes
data/script/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler"
4
+ Bundler.require :default, :development
5
+ require_relative "setup"
6
+
7
+ puts "Loading CachedRecord development environment (#{CachedRecord::VERSION})"
8
+ Pry.start
data/script/setup.rb ADDED
@@ -0,0 +1,37 @@
1
+ require "yaml"
2
+ require "logger"
3
+ require "active_record"
4
+
5
+ dbconfig = YAML.load_file(File.expand_path("../../config/database.yml", __FILE__))["development"]
6
+ logger = Logger.new("log/development.log")
7
+
8
+ ActiveRecord::Base.establish_connection dbconfig
9
+ ActiveRecord::Base.time_zone_aware_attributes = true
10
+ ActiveRecord::Base.default_timezone = :local
11
+ ActiveRecord::Base.logger = logger
12
+
13
+ CachedRecord.setup :redis
14
+ Redis.new.flushdb
15
+
16
+ class Article < ActiveRecord::Base
17
+ belongs_to :author, :class_name => "User"
18
+ has_many :comments
19
+ has_and_belongs_to_many :tags
20
+ as_memoized_cache :only => [:title], :include => [:author, :comments, :tags], :expire => 20.seconds
21
+ end
22
+
23
+ class User < ActiveRecord::Base
24
+ has_one :foo, :class_name => "Article", :foreign_key => "foo_id"
25
+ as_memoized_cache :only => [:name], :include => [:foo]
26
+ end
27
+
28
+ class Comment < ActiveRecord::Base
29
+ belongs_to :article
30
+ belongs_to :poster, :class_name => "User"
31
+ as_memoized_cache :only => [:content], :include => [:poster]
32
+ end
33
+
34
+ class Tag < ActiveRecord::Base
35
+ has_and_belongs_to_many :articles
36
+ as_memoized_cache :only => [:name]
37
+ end