cached_record 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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