arid_cache 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem 'activerecord', '=2.3.8'
5
+ gem 'activesupport', '=2.3.8'
6
+ gem 'sqlite3-ruby', '=1.3.1'
7
+
8
+ gem 'will_paginate', '=2.3.15'
9
+ gem 'jeweler', '=1.4.0'
10
+
11
+ gem 'ruby-debug', '=0.10.3'
12
+ gem 'ruby-debug-base', '=0.10.3'
13
+ gem 'machinist', '=1.0.6'
14
+ gem 'faker', '=0.3.1'
15
+ gem 'rspec', '=1.3.0', :require => 'spec'
16
+ gem 'test-unit', '=1.2.3'
17
+ gem 'mocha', '=0.9.8'
18
+ gem 'arid_cache', :path => "./"
data/Gemfile.lock ADDED
@@ -0,0 +1,59 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ arid_cache (1.0.2)
5
+ will_paginate
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activerecord (2.3.8)
11
+ activesupport (= 2.3.8)
12
+ activesupport (2.3.8)
13
+ columnize (0.3.1)
14
+ faker (0.3.1)
15
+ gemcutter (0.6.1)
16
+ git (1.2.5)
17
+ hoe (2.6.2)
18
+ rake (>= 0.8.7)
19
+ rubyforge (>= 2.0.4)
20
+ jeweler (1.4.0)
21
+ gemcutter (>= 0.1.0)
22
+ git (>= 1.2.5)
23
+ rubyforge (>= 2.0.0)
24
+ json_pure (1.4.6)
25
+ linecache (0.43)
26
+ machinist (1.0.6)
27
+ mocha (0.9.8)
28
+ rake
29
+ rake (0.8.7)
30
+ rspec (1.3.0)
31
+ ruby-debug (0.10.3)
32
+ columnize (>= 0.1)
33
+ ruby-debug-base (~> 0.10.3.0)
34
+ ruby-debug-base (0.10.3)
35
+ linecache (>= 0.3)
36
+ rubyforge (2.0.4)
37
+ json_pure (>= 1.1.7)
38
+ sqlite3-ruby (1.3.1)
39
+ test-unit (1.2.3)
40
+ hoe (>= 1.5.1)
41
+ will_paginate (2.3.15)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ activerecord (= 2.3.8)
48
+ activesupport (= 2.3.8)
49
+ arid_cache!
50
+ faker (= 0.3.1)
51
+ jeweler (= 1.4.0)
52
+ machinist (= 1.0.6)
53
+ mocha (= 0.9.8)
54
+ rspec (= 1.3.0)
55
+ ruby-debug (= 0.10.3)
56
+ ruby-debug-base (= 0.10.3)
57
+ sqlite3-ruby (= 1.3.1)
58
+ test-unit (= 1.2.3)
59
+ will_paginate (= 2.3.15)
data/README.rdoc CHANGED
@@ -9,9 +9,9 @@ AridCache simplifies caching by supporting auto-expiring cache keys - as well as
9
9
  == Install
10
10
 
11
11
  Add this to your <tt>config/environment.rb</tt> file:
12
-
12
+
13
13
  config.gem 'arid_cache'
14
-
14
+
15
15
  Then
16
16
 
17
17
  rake gems:install
@@ -28,7 +28,7 @@ The way you interact with the cache via your model methods is to prepend the met
28
28
 
29
29
  User.cached_count # cache key is arid-cache-user-count
30
30
  genre.cached_top_ten_tracks # cache key is arid-cache-genres/<id>-top_ten_tracks
31
-
31
+
32
32
  You can also define caches that use compositions of methods or named scopes, or other complex queries, without having to add a new method to your class. This way you can also create different caches that all use the same method. For example,
33
33
 
34
34
  # cache key is arid-cache-user-most_active_users
@@ -78,13 +78,13 @@ To dynamically define caches just pass a block to your <tt>cached_</tt> calls.
78
78
  User.cached_most_active_users do
79
79
  active.find(:order => 'activity DESC', :limit => 5)
80
80
  end
81
-
81
+
82
82
  => [#<User id: 23>, #<User id: 30>, #<User id: 5>, #<User id: 2>, #<User id: 101>]
83
-
83
+
84
84
  user.cached_favorite_pets do
85
85
  pets.find(:all, :conditions => { 'favorite' => true })
86
86
  end
87
-
87
+
88
88
  => [#<Pet id: 11>, #<Pet id: 21>, #<Pet id: 3>]
89
89
 
90
90
  === Configuring Caches on your Models
@@ -100,12 +100,12 @@ You can pass a hash of options to <tt>instance_caches</tt> and <tt>class_caches<
100
100
  popular(:limit => 10, :order => 'popularity DESC')
101
101
  end
102
102
  end
103
-
103
+
104
104
  instance_caches(:order => 'release_date DESC') do
105
105
  highlight_tracks(:include => [:album, :artist]) do
106
106
  cached_tracks(:limit => 10, :include => [:album, :artist])
107
107
  end
108
- highlight_artists(:order => nil) do # override the global :order option
108
+ highlight_artists(:order => nil) do # override the global :order option
109
109
  cached_artists(:limit => 10)
110
110
  end
111
111
  highlight_albums(:include => :artist) do
@@ -113,7 +113,7 @@ You can pass a hash of options to <tt>instance_caches</tt> and <tt>class_caches<
113
113
  end
114
114
  end
115
115
  end
116
-
116
+
117
117
  # app/controllers/genre_controller.rb
118
118
  @most_popular = Genre.cached_most_popular
119
119
  @tracks = @genre.cached_highlight_tracks
@@ -135,8 +135,8 @@ AridCache cache keys are defined based on the methods you call to interact with
135
135
 
136
136
  Album.cached_featured_albums => cache key is arid-cache-album-featured_albums
137
137
  album.cached_top_tracks => cache key is arid-cache-albums/<id>-top_tracks
138
-
139
- Caches on model instances can be set to automatically incorporate the ActiveRecord <tt>cache_key</tt> which includes the <tt>updated_at</tt> timestamp of that instance, making them auto-expire when the instance is updated.
138
+
139
+ Caches on model instances can be set to automatically incorporate the ActiveRecord <tt>cache_key</tt> which includes the <tt>updated_at</tt> timestamp of that instance, making them auto-expire when the instance is updated.
140
140
 
141
141
  To incorporate the the <tt>cache_key</tt> pass <b><tt>:auto_expire => true</tt></b> to your cache method:
142
142
 
@@ -153,22 +153,22 @@ If you need to examine values in the cache yourself you can build the AridCache
153
153
  Album.arid_cache_key('featured_albums') => arid-cache-album-featured_albums
154
154
  album.arid_cache_key('top_tracks') => arid-cache-albums/2-top_tracks
155
155
  album.arid_cache_key('top_tracks', :auto_expire => true) => arid-cache-albums/2-20091211120100-top_tracks
156
-
156
+
157
157
  == Managing your Caches
158
158
 
159
159
  === Deleting & Expiring Caches
160
160
 
161
161
  AridCache provides methods to help you clear your caches:
162
-
162
+
163
163
  AridCache.clear_caches => expires all AridCache caches
164
164
  Model.clear_caches => expires class and instance-level caches for this model
165
165
  Model.clear_instance_caches => expires instance-level caches for this model
166
166
  Model.clear_class_caches => expires class-level caches for this model
167
-
167
+
168
168
  The <tt>Model.clear_caches</tt> methods are also available on all model instances.
169
169
 
170
170
  <B>Your cache store needs to support the <tt>delete_matched</tt> method for the above to work. Currently MemCacheStore and MemoryStore do not.</b>
171
-
171
+
172
172
  Alternatively you can pass a <b><tt>:force => true</tt></b> option in your <tt>cached_</tt> calls to force a refresh of a particular cache, while still returning the refreshed results. For example:
173
173
 
174
174
  Album.cached_featured_albums(:force => true) => returns featured albums
@@ -190,7 +190,7 @@ If you would like to be able to pass more options to your cache store (like <tt>
190
190
 
191
191
  AridCache::CacheProxy::OPTIONS_FOR_CACHE.push(:raw, :unless_exist)
192
192
 
193
- == Extras
193
+ == Extras
194
194
 
195
195
  === Cached Counts
196
196
 
@@ -199,7 +199,7 @@ AridCache stores the count as well so the next time you request the count it
199
199
  just takes a single read from the cache.
200
200
 
201
201
  To get the count just append <tt>_count</tt> to your <tt>cached_</tt> call. For example, if we have a cache like <tt>album.cached_tracks</tt> we can get the count by calling,
202
-
202
+
203
203
  album.cached_tracks => returns an array of tracks
204
204
  album.cached_tracks_count => returns the count with a single read from the cache
205
205
 
@@ -207,7 +207,7 @@ This is also supported for your non-ActiveRecord collections if the collection <
207
207
 
208
208
  album.cached_similar_genres => returns ['Pop', 'Rock', 'Rockabilly']
209
209
  album.cached_similar_genres_count => returns 3
210
-
210
+
211
211
  Sometimes you may want the collection count without loading and caching the collection itself. AridCache is smart enough that if you only ask for a count it will only query for the count. This is only possible if the return value of your method is a named scope or association proxy (since these are lazy-loaded unlike a call to <tt>find()</tt>).
212
212
 
213
213
  In the example above if we only ever call <tt>album.cached_tracks_count</tt>, only the count will be cached. If we subsequently call <tt>album.cached_tracks</tt> the collection will be loaded and the IDs cached as per normal.
@@ -222,21 +222,37 @@ AridCache supports pagination using WillPaginate. If you are not changing the o
222
222
 
223
223
  An advantage of using AridCache is that since we already have the size of the collection in the cache no query is required to set the <tt>:total_entries</tt> on the <tt>WillPaginate::Collection</tt>.
224
224
 
225
- To paginate just pass a <tt>:page</tt> option in your call to <tt>cached_</tt>. If you don't pass a value for <tt>:per_page</tt> AridCache gets the value from <tt>Model.per_page</tt>.
225
+ To paginate just pass a <tt>:page</tt> option in your call to <tt>cached_</tt>. If you don't pass a value for <tt>:per_page</tt> AridCache gets the value from <tt>Model.per_page</tt>, which is what <tt>WillPaginate</tt> uses.
226
226
 
227
227
  Some examples of pagination:
228
228
 
229
229
  User.cached_active(:page => 1, :per_page => 30)
230
- User.cached_active(:page => 2) # uses User.per_page
230
+ User.cached_active(:page => 2) # uses User.per_page
231
231
  user.cached_pets(:page => 1) # uses Pet.per_page
232
232
 
233
- If you want to paginate using a different ordering, pass an <tt>:order</tt> option. Because the order is being changed AridCache cannot paginate in memory. Instead, the cached IDs are passed to your <tt>Model.paginate</tt> method along with any other options and the database will order the collection and apply limits etc.
233
+ If you want to paginate using a different ordering, pass an <tt>:order</tt> option. Because the order is being changed AridCache cannot paginate in memory. Instead, the cached IDs are passed to your <tt>Model.paginate</tt> method along with any other options and the database will order the collection, apply limits and offsets, etc. Because the number of records the database deals with is limited, this is still much, much faster than ordering over the whole table.
234
234
 
235
235
  For example, the following queries will work:
236
236
 
237
237
  user.cached_companies(:page => 1, :per_page => 3, :order => 'name DESC')
238
238
  user.cached_companies(:page => 1, :per_page => 3, :order => 'name ASC')
239
-
239
+
240
+ By specifying an <tt>:order</tt> option in our cached call we can get different "views" of the cached collection. I think this a "good thing". However, you need to be aware that in order to guarantee that the ordering you requested is the same as the order of the initial results (when the cache was primed), we have to order in the database. This results in two queries being executed the first time you query the cache (one to prime it and the other to order and return the results). If no order option is specified, we can skip the second query and do everything in memory.
241
+
242
+ If you have an expensive cache and don't want that extra query, just define a new cache with your desired ordering and use that. Make sure that the order of the initial results matches your desired ordering. Building on the example above we could do:
243
+
244
+ User.instance_caches do
245
+ companies_asc do
246
+ companies(:order => 'name ASC')
247
+ end
248
+ companies_desc do
249
+ companies(:order => 'name DESC')
250
+ end
251
+ end
252
+ user.cached_companies_asc(:page => 1, :per_page => 3)
253
+ user.cached_companies_desc(:page => 1, :per_page => 3)
254
+
255
+
240
256
  === Limit & Offset
241
257
 
242
258
  You apply <tt>:limit</tt> and <tt>:offset</tt> options in a similar manner to the <tt>:page</tt> and <tt>:per_page</tt> options. The limit and offset will be applied in memory and only the resulting subset selected from the target table - unless you specify a new order.
@@ -244,18 +260,18 @@ You apply <tt>:limit</tt> and <tt>:offset</tt> options in a similar manner to th
244
260
  user.cached_pets(:limit => 2, :include => :toys)
245
261
  user.cached_pets(:limit => 2, :offset => 3, :include => :toys)
246
262
  genre.cached_top_ten_tracks { cached_tracks(:limit => 10, :order => 'popularity DESC') }
247
-
263
+
248
264
  === Other Options to <tt>find</tt>
249
-
265
+
250
266
  You can pass options like <tt>:include</tt> (or any other valid <tt>find</tt> options) to augment the results of your cached query. Just because all of the options are supported, does not mean it's a good idea to use them, though. Take a look at your logs to see how AridCache is interacting with the cache and the database if you don't get the results you expect.
251
267
 
252
268
  For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> which gives the active users. We can call:
253
-
269
+
254
270
  User.cached_active.paginate(:page => 2, :per_page => 10, :include => :preferences)
255
271
  User.cached_active(:limit => 10, :offset => 10, :include => :preferences)
256
272
 
257
273
  (These calls return similar results, except that the first call returns a <tt>WillPaginate::Collection</tt> and the second just returns an <tt>Array</tt>.)
258
-
274
+
259
275
  == Efficiency
260
276
 
261
277
  * AridCache intercepts calls to <tt>cached_</tt> methods using <tt>method_missing</tt> then defines those methods on your models as they are called, so they bypass method missing on subsequent calls.
@@ -265,10 +281,10 @@ For example, assume we have a <tt>named_scope :active</tt> on <tt>User</tt> whic
265
281
 
266
282
  == Compatibility
267
283
 
268
- Tested on Ruby 1.8.6, 1.8.7 and 1.9.1.
284
+ Tested on Ruby 1.8.6, 1.8.7 and 1.9.1.
269
285
 
270
286
  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:
271
-
287
+
272
288
  Array.class_eval { alias count size }
273
289
 
274
290
  The version of Rails shouldn't matter much, but it's working on 2.3.4.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
- require 'rake'
2
+ require 'bundler/setup'
3
+ Bundler.require
3
4
 
4
5
  begin
5
6
  require 'jeweler'
@@ -25,19 +26,19 @@ rescue LoadError
25
26
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
26
27
  end
27
28
 
28
- # require 'spec/rake/spectask'
29
- # Spec::Rake::SpecTask.new(:spec) do |spec|
30
- # spec.libs << 'lib' << 'spec'
31
- # spec.spec_files = FileList['spec/**/*_spec.rb']
32
- # end
33
- #
34
- # Spec::Rake::SpecTask.new(:rcov) do |spec|
35
- # spec.libs << 'lib' << 'spec'
36
- # spec.pattern = 'spec/**/*_spec.rb'
37
- # spec.rcov = true
38
- # end
39
- #
40
- # task :spec => :check_dependencies
29
+ require 'spec/rake/spectask'
30
+ Spec::Rake::SpecTask.new(:spec) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.spec_files = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
36
+ spec.libs << 'lib' << 'spec'
37
+ spec.pattern = 'spec/**/*_spec.rb'
38
+ spec.rcov = true
39
+ end
40
+
41
+ task :spec => :check_dependencies
41
42
 
42
43
  require 'rake/testtask'
43
44
  Rake::TestTask.new(:test) do |test|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.2
1
+ 1.0.3
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.0.2"
8
+ s.version = "1.0.3"
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{2010-09-07}
12
+ s.date = %q{2010-10-21}
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
  }
@@ -20,6 +20,8 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
20
20
  ]
21
21
  s.files = [
22
22
  ".gitignore",
23
+ "Gemfile",
24
+ "Gemfile.lock",
23
25
  "LICENSE",
24
26
  "README.rdoc",
25
27
  "Rakefile",
@@ -35,17 +37,21 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
35
37
  "spec/arid_cache_spec.rb",
36
38
  "spec/spec.opts",
37
39
  "spec/spec_helper.rb",
40
+ "spec/support/ar_query.rb",
41
+ "spec/support/custom_methods.rb",
42
+ "spec/support/matchers.rb",
38
43
  "tasks/arid_cache_tasks.rake",
39
44
  "test/arid_cache_test.rb",
40
45
  "test/console",
41
- "test/db/prepare.rb",
42
- "test/db/schema.rb",
43
- "test/lib/active_support/cache/file_store_extras.rb",
46
+ "test/lib/add_query_counting_to_active_record.rb",
44
47
  "test/lib/blueprint.rb",
48
+ "test/lib/db_prepare.rb",
49
+ "test/lib/fix_active_support_file_store_expires_in.rb",
50
+ "test/lib/mock_rails.rb",
51
+ "test/lib/models/company.rb",
52
+ "test/lib/models/empty_user_relation.rb",
53
+ "test/lib/models/user.rb",
45
54
  "test/log/.gitignore",
46
- "test/models/company.rb",
47
- "test/models/empty_user_relation.rb",
48
- "test/models/user.rb",
49
55
  "test/test_helper.rb"
50
56
  ]
51
57
  s.homepage = %q{http://github.com/kjvarga/arid_cache}
@@ -56,14 +62,18 @@ AridCache is designed for handling large, expensive ActiveRecord collections but
56
62
  s.test_files = [
57
63
  "spec/arid_cache_spec.rb",
58
64
  "spec/spec_helper.rb",
65
+ "spec/support/ar_query.rb",
66
+ "spec/support/custom_methods.rb",
67
+ "spec/support/matchers.rb",
59
68
  "test/arid_cache_test.rb",
60
- "test/db/prepare.rb",
61
- "test/db/schema.rb",
62
- "test/lib/active_support/cache/file_store_extras.rb",
69
+ "test/lib/add_query_counting_to_active_record.rb",
63
70
  "test/lib/blueprint.rb",
64
- "test/models/company.rb",
65
- "test/models/empty_user_relation.rb",
66
- "test/models/user.rb",
71
+ "test/lib/db_prepare.rb",
72
+ "test/lib/fix_active_support_file_store_expires_in.rb",
73
+ "test/lib/mock_rails.rb",
74
+ "test/lib/models/company.rb",
75
+ "test/lib/models/empty_user_relation.rb",
76
+ "test/lib/models/user.rb",
67
77
  "test/test_helper.rb"
68
78
  ]
69
79
 
@@ -68,6 +68,11 @@ module AridCache
68
68
 
69
69
  self.cache_key = object.arid_cache_key(key, opts_for_cache_key)
70
70
  self.cached = Rails.cache.read(cache_key, opts_for_cache)
71
+ self.klass = if self.cached && self.cached.is_a?(AridCache::CacheProxy::Result) # infer class of the results to return
72
+ self.cached.klass
73
+ else
74
+ object_base_class
75
+ end
71
76
  end
72
77
 
73
78
  def fetch_count
@@ -89,10 +94,6 @@ module AridCache
89
94
  execute_find
90
95
  elsif cached.is_a?(AridCache::CacheProxy::Result)
91
96
  if cached.has_ids?
92
-
93
- # Infer the class of the objects we are returning
94
- self.klass = cached.klass || object_base_class
95
-
96
97
  fetch_from_cache
97
98
  else
98
99
  execute_find
@@ -116,11 +117,9 @@ module AridCache
116
117
  if paginate?
117
118
 
118
119
  # Return a paginated collection
119
-
120
120
  if cached.ids.empty?
121
121
 
122
122
  # No ids, return an empty WillPaginate result
123
-
124
123
  [].paginate(opts_for_paginate)
125
124
 
126
125
  elsif combined_options.include?(:order)
@@ -128,14 +127,12 @@ module AridCache
128
127
  # An order has been specified. We have to go to the database
129
128
  # and paginate there because the contents of the requested
130
129
  # page will be different.
131
-
132
130
  klass.paginate(cached.ids, { :total_entries => cached.ids.size }.merge(opts_for_find.merge(opts_for_paginate)))
133
131
 
134
132
  else
135
133
 
136
134
  # Order is unchanged. We can paginate in memory and only select
137
135
  # those ids that we actually need. This is the most efficient.
138
-
139
136
  paged_ids = cached.ids.paginate(opts_for_paginate)
140
137
  paged_ids.replace(klass.find_all_by_id(paged_ids, opts_for_find(paged_ids)))
141
138
 
@@ -146,13 +143,11 @@ module AridCache
146
143
  # We are returning a regular (non-paginated) result.
147
144
  # If we don't have any ids, that's an empty result
148
145
  # in any language.
149
-
150
146
  []
151
147
 
152
148
  elsif combined_options.include?(:order)
153
149
 
154
150
  # An order has been specified, so use it.
155
-
156
151
  klass.find_all_by_id(cached.ids, opts_for_find)
157
152
 
158
153
  else
@@ -160,7 +155,6 @@ module AridCache
160
155
  # No order has been specified, so we have to maintain
161
156
  # the current order. We do this by passing some extra
162
157
  # SQL which orders by the current array ordering.
163
-
164
158
  offset, limit = combined_options.delete(:offset) || 0, combined_options.delete(:limit) || cached.count
165
159
  ids = cached.ids[offset, limit]
166
160
 
@@ -201,7 +195,14 @@ module AridCache
201
195
  Rails.cache.write(cache_key, cached, opts_for_cache)
202
196
 
203
197
  self.cached = cached
204
- return_records(records)
198
+ # An order has been specified. We have to go to the database
199
+ # to order because we can't be sure that the current order is the same as the cache.
200
+ if cached.is_a?(AridCache::CacheProxy::Result) && combined_options.include?(:order)
201
+ self.klass = self.cached.klass # TODO used by fetch_from_cache needs refactor
202
+ fetch_from_cache
203
+ else
204
+ process_result_in_memory(records)
205
+ end
205
206
  end
206
207
 
207
208
  # Convert records to an array before calling paginate. If we don't do this
@@ -222,7 +223,7 @@ module AridCache
222
223
  # to define it in a block and have a new cache for it:
223
224
  #
224
225
  # model.my_special_collection { the_collection(:order => 'new order', :limit => 10) }
225
- def return_records(records)
226
+ def process_result_in_memory(records)
226
227
  if opts.include?(:page)
227
228
  records = records.respond_to?(:to_a) ? records.to_a : records
228
229
  records.respond_to?(:paginate) ? records.paginate(opts_for_paginate) : records
@@ -1,7 +1,39 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "AridCache" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
4
+ describe 'results' do
5
+ before :each do
6
+ Company.destroy_all
7
+ @company1 = Company.make(:name => 'a')
8
+ @company2 = Company.make(:name => 'b')
9
+ Company.class_caches do
10
+ ordered_by_name { Company.all(:order => 'name ASC') }
11
+ end
12
+ Company.clear_caches
13
+ end
14
+
15
+ it "order should match the original order" do
16
+ 3.times do |t|
17
+ results = Company.cached_ordered_by_name
18
+ results.size.should == 2
19
+ results[0].name.should == @company1.name
20
+ results[1].name.should == @company2.name
21
+ end
22
+ end
23
+
24
+ it "order should match the order option" do
25
+ 3.times do |t|
26
+ results = Company.cached_ordered_by_name(:order => 'name DESC')
27
+ results.size.should == 2
28
+ results[0].name.should == @company2.name
29
+ results[1].name.should == @company1.name
30
+ end
31
+ end
32
+
33
+ it "with order option should go to the database to order" do
34
+ lambda {
35
+ Company.cached_ordered_by_name(:order => 'name DESC')
36
+ }.should query(2)
37
+ end
6
38
  end
7
39
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,29 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'arid_cache'
4
- require 'spec'
1
+ root_path = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+ $LOAD_PATH.unshift(File.join(root_path, '/test/lib')) # make requiring from test/lib easy
3
+
4
+ require 'bundler/setup'
5
+ Bundler.require
6
+
5
7
  require 'spec/autorun'
8
+ require 'mock_rails'
9
+ require 'blueprint'
10
+
11
+ WillPaginate.enable_activerecord
12
+ AridCache.init_rails
6
13
 
7
- require 'rubygems'
8
- require 'will_paginate'
14
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
9
15
 
10
16
  Spec::Runner.configure do |config|
11
-
12
- end
17
+ include ActiveRecordQueryMatchers
18
+ config.mock_with :mocha
19
+
20
+ config.before(:all) do
21
+ Sham.reset(:before_all)
22
+ end
23
+
24
+ config.before(:each) do
25
+ Sham.reset(:before_each)
26
+ full_example_description = "#{self.class.description} #{@method_name}"
27
+ RAILS_DEFAULT_LOGGER.info("\n\n#{full_example_description}\n#{'-' * (full_example_description.length)}")
28
+ end
29
+ end
@@ -0,0 +1,128 @@
1
+ module ActiveRecordQueryMatchers
2
+
3
+ class ArQuery #:nodoc:
4
+ cattr_accessor :executed
5
+
6
+ @@recording_queries = false
7
+ def self.recording_queries?
8
+ @@recording_queries
9
+ end
10
+
11
+ def initialize(expected, &block)
12
+ @expected = expected
13
+ @block = block
14
+ end
15
+
16
+ def matches?(given_proc)
17
+ @eval_block = false
18
+ @eval_error = nil
19
+ ArQuery.executed = []
20
+ @@recording_queries = true
21
+
22
+ given_proc.call
23
+
24
+ if @expected.is_a?(Fixnum)
25
+ @actual = ArQuery.executed.length
26
+ @matched = @actual == @expected
27
+ else
28
+ @actual = ArQuery.executed.detect { |sql| @expected === sql }
29
+ @matched = !@actual.nil?
30
+ end
31
+
32
+ eval_block if @block && @matched && !negative_expectation?
33
+
34
+ ensure
35
+ ArQuery.executed = nil
36
+ @@recording_queries = false
37
+ return @matched && @eval_error.nil?
38
+ end
39
+
40
+ def eval_block
41
+ @eval_block = true
42
+ begin
43
+ @block[ArQuery.executed]
44
+ rescue Exception => err
45
+ @eval_error = err
46
+ end
47
+ end
48
+
49
+ def failure_message_for_should
50
+ if @eval_error
51
+ @eval_error.message
52
+ elsif @expected.is_a?(Fixnum)
53
+ "expected #{@expected}, got #{@actual}"
54
+ else
55
+ "expected to execute a query with pattern #{@expected.inspect}, but it wasn't"
56
+ end
57
+ end
58
+
59
+ def failure_message_for_should_not
60
+ if @expected.is_a?(Fixnum)
61
+ "did not expect #{@expected}"
62
+ else
63
+ "did not expect to execute a query with pattern #{@expected.inspect}, but it was executed"
64
+ end
65
+ end
66
+
67
+ def description
68
+ if @expected.is_a?(Fixnum)
69
+ @expected == 1 ? "execute 1 query" : "execute #{@expected} queries"
70
+ else
71
+ "execute query with pattern #{@expected.inspect}"
72
+ end
73
+ end
74
+
75
+ # Copied from raise_error
76
+ def negative_expectation?
77
+ @negative_expectation ||= !caller.first(3).find { |s| s =~ /should_not/ }.nil?
78
+ end
79
+
80
+ end
81
+
82
+ unless defined?(IGNORED_SQL)
83
+ # From active_record/test/cases/helper.rb :
84
+ ActiveRecord::Base.connection.class.class_eval do
85
+ IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
86
+ def execute_with_query_record(sql, name = nil, &block)
87
+ if ArQuery.recording_queries?
88
+ ArQuery.executed << sql unless IGNORED_SQL.any? { |ignore| sql =~ ignore }
89
+ end
90
+ execute_without_query_record(sql, name, &block)
91
+ end
92
+ alias_method_chain :execute, :query_record
93
+ end
94
+ end
95
+
96
+ # :call-seq:
97
+ # response.should query
98
+ # response.should query(expected)
99
+ # response.should query(expected) { |sql| ... }
100
+ # response.should_not query
101
+ # response.should_not query(expected)
102
+ #
103
+ # Accepts a Fixnum or a Regexp as argument.
104
+ #
105
+ # With no args, matches if exactly 1 query is executed.
106
+ # With a Fixnum arg, matches if the number of queries executed equals the given number.
107
+ # With a Regexp arg, matches if any query is executed with the given pattern.
108
+ #
109
+ # Pass an optional block to perform extra verifications of the queries matched.
110
+ # The argument of the block will receive an array of query strings that were executed.
111
+ #
112
+ # == Examples
113
+ #
114
+ # lambda { @object.posts }.should query
115
+ # lambda { @object.valid? }.should query(0)
116
+ # lambda { @object.save }.should query(3)
117
+ # lambda { @object.line_items }.should query(/SELECT DISTINCT/)
118
+ # lambda { @object.line_items }.should query(1) { |sql| sql[0].should =~ /SELECT DISTINCT/ }
119
+ #
120
+ # lambda { @object.posts }.should_not query
121
+ # lambda { @object.valid? }.should_not query(0)
122
+ # lambda { @object.save }.should_not query(3)
123
+ # lambda { @object.line_items }.should_not query(/SELECT DISTINCT/)
124
+ #
125
+ def query(expected = 1, &block)
126
+ ArQuery.new(expected, &block)
127
+ end
128
+ end
@@ -0,0 +1,7 @@
1
+ class String
2
+
3
+ def json_to_hash
4
+ HashWithIndifferentAccess.new JSON.parse(self)
5
+ end
6
+
7
+ end
@@ -0,0 +1,33 @@
1
+ Spec::Matchers.define :include_keys do |*expected|
2
+
3
+ match do |actual|
4
+ check_all_present(actual, expected) == []
5
+ end
6
+
7
+ failure_message_for_should do |actual|
8
+ "expected key #{check_all_present(actual, expected).first} but did not see it in #{actual.keys.map(&:to_sym)}"
9
+ end
10
+
11
+ def check_all_present actual, expected
12
+ keys_we_have = actual.keys.map(&:to_sym)
13
+ expected = [expected] unless expected.is_a?(Array)
14
+ remainder = expected.map(&:to_sym) - keys_we_have
15
+ end
16
+ end
17
+
18
+ Spec::Matchers.define :match_object do |object, *expected_matching_keys|
19
+
20
+ match do |actual|
21
+ check_specified_keys_match(actual, object, expected_matching_keys) == []
22
+ end
23
+
24
+ failure_message_for_should do |actual|
25
+ offending_key = check_specified_keys_match(actual, object, expected_matching_keys).first
26
+ "expected match for #{offending_key} but it did not. Expected: #{object.send(offending_key).inspect} but got #{actual[offending_key].inspect}"
27
+ end
28
+
29
+ def check_specified_keys_match actual, object, expected_matches
30
+ expected_matches = [expected_matches] unless expected_matches.is_a?(Array)
31
+ expected_matches.map(&:to_sym).map { |key| key unless (object.send(key) == actual[key]) }.compact
32
+ end
33
+ end
@@ -76,7 +76,7 @@ class AridCacheTest < ActiveSupport::TestCase
76
76
  end
77
77
 
78
78
  test "passes on options to find" do
79
- actual = @user.cached_companies(:order => 'users.id DESC')
79
+ actual = @user.cached_companies(:order => 'users.id DESC', :include => [:owner])
80
80
  expected = @user.companies
81
81
  assert_equal expected, actual
82
82
  assert_equal expected.first, actual.first
data/test/console CHANGED
@@ -1,8 +1,15 @@
1
- #!/usr/bin/env ruby
2
- irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
- libs = []
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
4
2
 
5
- libs << 'irb/completion'
6
- libs << 'test_helper'
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ Bundler.require
6
+ require 'mock_rails'
7
+ require 'blueprint'
8
+ require 'irb'
7
9
 
8
- exec "#{irb} -Ilib:test#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"
10
+ WillPaginate.enable_activerecord
11
+ AridCache.init_rails
12
+ Blueprint.seeds
13
+
14
+ ARGV.clear
15
+ IRB.start
@@ -0,0 +1,11 @@
1
+ class << ActiveRecord::Base.connection
2
+ IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
3
+
4
+ def execute_with_counting(sql, name = nil, &block)
5
+ $query_count ||= 0
6
+ $query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
7
+ execute_without_counting(sql, name, &block)
8
+ end
9
+
10
+ alias_method_chain :execute, :counting
11
+ end
@@ -0,0 +1,34 @@
1
+ require 'active_record'
2
+
3
+ # Create an in-memory sqlite3 database
4
+ ActiveRecord::Base.establish_connection(
5
+ :adapter => "sqlite3",
6
+ :database => ":memory:"
7
+ )
8
+
9
+ # Schema
10
+ ActiveRecord::Base.silence do
11
+ ActiveRecord::Migration.verbose = false
12
+
13
+ ActiveRecord::Schema.define do
14
+ create_table "users", :force => true do |t|
15
+ t.column "name", :text
16
+ t.column "email", :text
17
+ t.timestamps
18
+ end
19
+
20
+ create_table "companies", :force => true do |t|
21
+ t.column "name", :text
22
+ t.column "owner_id", :integer
23
+ t.column "country_id", :integer
24
+ t.column "employees", :integer
25
+ t.timestamps
26
+ end
27
+
28
+ create_table "empty_user_relations", :force => true do |t|
29
+ t.column "user_id", :integer
30
+ end
31
+ end
32
+ end
33
+
34
+ Dir[File.join(File.dirname(__FILE__), 'models', '*.rb')].each { |f| require f }
@@ -1,3 +1,4 @@
1
+ # Add support for expiring file-cache store.
1
2
 
2
3
  module ActiveSupport
3
4
  module Cache
@@ -0,0 +1,29 @@
1
+ require 'logger'
2
+ require 'fix_active_support_file_store_expires_in'
3
+
4
+ root_path = File.expand_path(File.join(File.dirname(__FILE__), '../../'))
5
+ RAILS_DEFAULT_LOGGER = ENV["STDOUT"] ? Logger.new(STDOUT) : Logger.new(File.join(root_path, '/test/log/test.log'))
6
+ RAILS_CACHE = ActiveSupport::Cache.lookup_store(:file_store, File.join(root_path, '/tmp/cache'))
7
+
8
+ # Mock Rails
9
+ Rails = Class.new do
10
+ cattr_accessor :logger, :cache
11
+ def self.cache
12
+ return RAILS_CACHE
13
+ end
14
+ def self.logger
15
+ return RAILS_DEFAULT_LOGGER
16
+ end
17
+ end
18
+
19
+ # Set loggers for all frameworks
20
+ for framework in ([ :active_record, :action_controller, :action_mailer ])
21
+ if Object.const_defined?(framework.to_s.camelize)
22
+ framework.to_s.camelize.constantize.const_get("Base").logger = Rails.logger
23
+ end
24
+ end
25
+ ActiveSupport::Dependencies.logger = Rails.logger
26
+ Rails.cache.logger = Rails.logger
27
+
28
+ # Include this last otherwise the logger isn't set properly
29
+ require 'db_prepare'
File without changes
File without changes
File without changes
data/test/test_helper.rb CHANGED
@@ -1,57 +1,19 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) # AridCache lib
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) # test lib
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) # make requiring from test/lib easy
4
2
 
5
3
  require 'fileutils'
6
4
  require 'rubygems'
7
- require 'active_record'
8
- require 'active_support'
9
- require 'active_support/test_case'
5
+ require 'bundler/setup'
6
+ Bundler.require
10
7
  require 'test/unit' # required by ActiveSupport::TestCase
11
- require 'will_paginate'
12
- require 'ruby-debug'
13
-
14
- # Activate Will Paginate
15
- WillPaginate.enable_activerecord
8
+ require 'active_support/test_case'
16
9
 
17
- # Add support for expiring file-cache store.
18
- require 'active_support/cache/file_store_extras'
10
+ require 'mock_rails'
11
+ require 'blueprint'
12
+ require 'add_query_counting_to_active_record'
19
13
 
20
- # Activate AridCache
21
- require 'arid_cache'
14
+ WillPaginate.enable_activerecord
22
15
  AridCache.init_rails
23
-
24
- # Setup logging
25
- log = File.expand_path(File.join(File.dirname(__FILE__), 'log', 'test.log'))
26
- RAILS_DEFAULT_LOGGER = ENV["STDOUT"] ? Logger.new(STDOUT) : Logger.new(log)
27
-
28
- # Setup the cache. Use the file-store cache because the
29
- # memory-store cache doesn't delete cache keys...I don't know why.
30
- RAILS_CACHE = ActiveSupport::Cache.lookup_store(:file_store, "#{File.dirname(__FILE__)}/tmp/cache")
31
-
32
- # Mock Rails
33
- Rails = Class.new do
34
- cattr_accessor :logger, :cache
35
- def self.cache
36
- return RAILS_CACHE
37
- end
38
- def self.logger
39
- return RAILS_DEFAULT_LOGGER
40
- end
41
- end
42
-
43
- # Set loggers for all frameworks
44
- for framework in ([ :active_record, :action_controller, :action_mailer ])
45
- if Object.const_defined?(framework.to_s.camelize)
46
- framework.to_s.camelize.constantize.const_get("Base").logger = Rails.logger
47
- end
48
- end
49
- ActiveSupport::Dependencies.logger = Rails.logger
50
- Rails.cache.logger = Rails.logger
51
-
52
- # Include this last otherwise the logger isn't set properly
53
- require 'db/prepare'
16
+ Blueprint.seeds
54
17
 
55
18
  ActiveRecord::Base.logger.info("#{"="*25} RUNNING UNIT TESTS #{"="*25}\n\t\t\t#{Time.now.to_s}\n#{"="*70}")
56
-
57
19
  Array.class_eval { alias count size } if RUBY_VERSION < '1.8.7'
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: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 2
10
- version: 1.0.2
9
+ - 3
10
+ version: 1.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Karl Varga
@@ -15,13 +15,14 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-07 00:00:00 -07:00
18
+ date: 2010-10-21 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: will_paginate
23
22
  prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
23
+ type: :runtime
24
+ name: will_paginate
25
+ version_requirements: &id001 !ruby/object:Gem::Requirement
25
26
  none: false
26
27
  requirements:
27
28
  - - ">="
@@ -30,12 +31,12 @@ dependencies:
30
31
  segments:
31
32
  - 0
32
33
  version: "0"
33
- type: :runtime
34
- version_requirements: *id001
34
+ requirement: *id001
35
35
  - !ruby/object:Gem::Dependency
36
- name: will_paginate
37
36
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
37
+ type: :development
38
+ name: will_paginate
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ">="
@@ -44,12 +45,12 @@ dependencies:
44
45
  segments:
45
46
  - 0
46
47
  version: "0"
47
- type: :development
48
- version_requirements: *id002
48
+ requirement: *id002
49
49
  - !ruby/object:Gem::Dependency
50
- name: faker
51
50
  prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
51
+ type: :development
52
+ name: faker
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
53
54
  none: false
54
55
  requirements:
55
56
  - - ">="
@@ -58,12 +59,12 @@ dependencies:
58
59
  segments:
59
60
  - 0
60
61
  version: "0"
61
- type: :development
62
- version_requirements: *id003
62
+ requirement: *id003
63
63
  - !ruby/object:Gem::Dependency
64
- name: machinist
65
64
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
65
+ type: :development
66
+ name: machinist
67
+ version_requirements: &id004 !ruby/object:Gem::Requirement
67
68
  none: false
68
69
  requirements:
69
70
  - - ">="
@@ -72,8 +73,7 @@ dependencies:
72
73
  segments:
73
74
  - 0
74
75
  version: "0"
75
- type: :development
76
- version_requirements: *id004
76
+ requirement: *id004
77
77
  description: |
78
78
  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.
79
79
  AridCache is designed for handling large, expensive ActiveRecord collections but is equally useful for caching anything else as well.
@@ -88,6 +88,8 @@ extra_rdoc_files:
88
88
  - README.rdoc
89
89
  files:
90
90
  - .gitignore
91
+ - Gemfile
92
+ - Gemfile.lock
91
93
  - LICENSE
92
94
  - README.rdoc
93
95
  - Rakefile
@@ -103,17 +105,21 @@ files:
103
105
  - spec/arid_cache_spec.rb
104
106
  - spec/spec.opts
105
107
  - spec/spec_helper.rb
108
+ - spec/support/ar_query.rb
109
+ - spec/support/custom_methods.rb
110
+ - spec/support/matchers.rb
106
111
  - tasks/arid_cache_tasks.rake
107
112
  - test/arid_cache_test.rb
108
113
  - test/console
109
- - test/db/prepare.rb
110
- - test/db/schema.rb
111
- - test/lib/active_support/cache/file_store_extras.rb
114
+ - test/lib/add_query_counting_to_active_record.rb
112
115
  - test/lib/blueprint.rb
116
+ - test/lib/db_prepare.rb
117
+ - test/lib/fix_active_support_file_store_expires_in.rb
118
+ - test/lib/mock_rails.rb
119
+ - test/lib/models/company.rb
120
+ - test/lib/models/empty_user_relation.rb
121
+ - test/lib/models/user.rb
113
122
  - test/log/.gitignore
114
- - test/models/company.rb
115
- - test/models/empty_user_relation.rb
116
- - test/models/user.rb
117
123
  - test/test_helper.rb
118
124
  has_rdoc: true
119
125
  homepage: http://github.com/kjvarga/arid_cache
@@ -152,12 +158,16 @@ summary: Automates efficient caching of your ActiveRecord collections, gives you
152
158
  test_files:
153
159
  - spec/arid_cache_spec.rb
154
160
  - spec/spec_helper.rb
161
+ - spec/support/ar_query.rb
162
+ - spec/support/custom_methods.rb
163
+ - spec/support/matchers.rb
155
164
  - test/arid_cache_test.rb
156
- - test/db/prepare.rb
157
- - test/db/schema.rb
158
- - test/lib/active_support/cache/file_store_extras.rb
165
+ - test/lib/add_query_counting_to_active_record.rb
159
166
  - test/lib/blueprint.rb
160
- - test/models/company.rb
161
- - test/models/empty_user_relation.rb
162
- - test/models/user.rb
167
+ - test/lib/db_prepare.rb
168
+ - test/lib/fix_active_support_file_store_expires_in.rb
169
+ - test/lib/mock_rails.rb
170
+ - test/lib/models/company.rb
171
+ - test/lib/models/empty_user_relation.rb
172
+ - test/lib/models/user.rb
163
173
  - test/test_helper.rb
data/test/db/prepare.rb DELETED
@@ -1,33 +0,0 @@
1
- require 'active_record'
2
- require 'active_record/fixtures'
3
-
4
- # Create an in-memory test database and load the fixures into it
5
- ActiveRecord::Base.establish_connection(
6
- :adapter => "sqlite3",
7
- :database => ":memory:"
8
- )
9
-
10
- # Schema
11
- ActiveRecord::Base.silence do
12
- ActiveRecord::Migration.verbose = false
13
- load(File.join(File.dirname(__FILE__), 'schema.rb'))
14
- end
15
-
16
- # Models
17
- Dir[File.join(File.dirname(__FILE__), '..', 'models', '*.rb')].each { |f| require f }
18
-
19
- # Populate
20
- require 'blueprint'
21
- Blueprint.seeds
22
-
23
- class << ActiveRecord::Base.connection
24
- IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SHOW FIELDS /]
25
-
26
- def execute_with_counting(sql, name = nil, &block)
27
- $query_count ||= 0
28
- $query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
29
- execute_without_counting(sql, name, &block)
30
- end
31
-
32
- alias_method_chain :execute, :counting
33
- end
data/test/db/schema.rb DELETED
@@ -1,19 +0,0 @@
1
- ActiveRecord::Schema.define do
2
- create_table "users", :force => true do |t|
3
- t.column "name", :text
4
- t.column "email", :text
5
- t.timestamps
6
- end
7
-
8
- create_table "companies", :force => true do |t|
9
- t.column "name", :text
10
- t.column "owner_id", :integer
11
- t.column "country_id", :integer
12
- t.column "employees", :integer
13
- t.timestamps
14
- end
15
-
16
- create_table "empty_user_relations", :force => true do |t|
17
- t.column "user_id", :integer
18
- end
19
- end