simply_stored 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,6 +1,33 @@
1
1
  Changelog
2
2
  =============
3
3
 
4
+ - Support :order option to has_many associations and classes, e.g:
5
+
6
+ @user.posts(:order => :desc)
7
+ Post.find(:all, :order => :desc)
8
+
9
+ Default to the CouchDB default of ascending.
10
+
11
+
12
+ - Support :limit option to has_many and has_many :through associations, e.g:
13
+
14
+ @user.posts(:limit => 5)
15
+
16
+ 0.1.14
17
+ =============
18
+
19
+ - Add ability to delete all design documents:
20
+
21
+ SimplyStored::Couch.delete_all_design_documents('http://localhost:5984/mydbname')
22
+
23
+ - Add rake tasks to delete all design documents. In your Rakefile:
24
+
25
+ require 'simply_stored/rake'
26
+
27
+ Then you can delete all design documents in a database like this:
28
+
29
+ DATABASE=http://localhost:5984/mydb rake simply_stored:delete_design_documents
30
+
4
31
  0.1.13
5
32
  =============
6
33
 
data/README.md CHANGED
@@ -119,6 +119,12 @@ The supported associations are: belongs_to, has_one, has_many, and has_many :thr
119
119
  post.comments
120
120
  # => [mikes_comment, johns_comment]
121
121
 
122
+ post.comments(:order => :desc)
123
+ # => [johns_comment, mikes_comment]
124
+
125
+ post.comments(:limit => 1)
126
+ # => [mikes_comment]
127
+
122
128
  post.comment_count
123
129
  # => 2
124
130
 
@@ -9,18 +9,25 @@ module SimplyStored
9
9
  define_method(name) do |*args|
10
10
  options = args.first && args.first.is_a?(Hash) && args.first
11
11
  if options
12
- options.assert_valid_keys(:force_reload, :with_deleted)
13
- forced_reload = options[:force_reload]
12
+ options.assert_valid_keys(:force_reload, :with_deleted, :limit, :order)
13
+ forced_reload = options.delete(:force_reload)
14
14
  with_deleted = options[:with_deleted]
15
+ limit = options[:limit]
16
+ descending = (options[:order] == :desc) ? true : false
15
17
  else
16
18
  forced_reload = false
17
19
  with_deleted = false
20
+ limit = nil
21
+ descending = false
18
22
  end
19
23
 
20
- if forced_reload || instance_variable_get("@#{name}").nil?
21
- instance_variable_set("@#{name}", find_associated(name, self.class, :with_deleted => with_deleted))
24
+ cached_results = cached_results = send("_get_cached_#{name}")
25
+ cache_key = _cache_key_for(options)
26
+ if forced_reload || cached_results[cache_key].nil?
27
+ cached_results[cache_key] = find_associated(name, self.class, :with_deleted => with_deleted, :limit => limit, :descending => descending)
28
+ instance_variable_set("@#{name}", cached_results)
22
29
  end
23
- instance_variable_get("@#{name}")
30
+ cached_results[cache_key]
24
31
  end
25
32
  end
26
33
 
@@ -30,26 +37,31 @@ module SimplyStored
30
37
  define_method(name) do |*args|
31
38
  options = args.first && args.first.is_a?(Hash) && args.first
32
39
  if options
33
- options.assert_valid_keys(:force_reload, :with_deleted)
40
+ options.assert_valid_keys(:force_reload, :with_deleted, :limit)
34
41
  forced_reload = options[:force_reload]
35
42
  with_deleted = options[:with_deleted]
43
+ limit = options[:limit]
36
44
  else
37
45
  forced_reload = false
38
46
  with_deleted = false
47
+ limit = nil
39
48
  end
40
49
 
41
- if forced_reload || instance_variable_get("@#{name}").nil?
50
+ cached_results = send("_get_cached_#{name}")
51
+ cache_key = _cache_key_for(options)
52
+
53
+ if forced_reload || cached_results[cache_key].nil?
42
54
 
43
55
  # there is probably a faster way to query this
44
- intermediate_objects = find_associated(through, self.class, :with_deleted => with_deleted)
56
+ intermediate_objects = find_associated(through, self.class, :with_deleted => with_deleted, :limit => limit)
45
57
 
46
58
  through_objects = intermediate_objects.map do |intermediate_object|
47
59
  intermediate_object.send(name.to_s.singularize.underscore, :with_deleted => with_deleted)
48
60
  end.flatten.uniq
49
-
50
- instance_variable_set("@#{name}", through_objects)
61
+ cached_results[cache_key] = through_objects
62
+ instance_variable_set("@#{name}", cached_results)
51
63
  end
52
- instance_variable_get("@#{name}")
64
+ cached_results[cache_key]
53
65
  end
54
66
  end
55
67
 
@@ -59,9 +71,11 @@ module SimplyStored
59
71
  raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
60
72
 
61
73
  value.send("#{self.class.foreign_key}=", id)
62
- value.save
63
- cached_version = instance_variable_get("@#{name}") || []
64
- instance_variable_set("@#{name}", cached_version << value)
74
+ value.save(false)
75
+
76
+ cached_results = send("_get_cached_#{name}")[:all]
77
+ send("_set_cached_#{name}", (cached_results || []) << value, :all)
78
+ nil
65
79
  end
66
80
  end
67
81
 
@@ -78,8 +92,9 @@ module SimplyStored
78
92
  value.save
79
93
  end
80
94
 
81
- cached_version = instance_variable_get("@#{name}") || []
82
- instance_variable_set("@#{name}", cached_version.delete_if{|item| item.id == value.id})
95
+ cached_results = send("_get_cached_#{name}")[:all]
96
+ send("_set_cached_#{name}", (cached_results || []).delete_if{|item| item.id == value.id}, :all)
97
+ nil
83
98
  end
84
99
  end
85
100
 
@@ -113,6 +128,22 @@ module SimplyStored
113
128
  end
114
129
  end
115
130
 
131
+ def define_cache_accessors(name)
132
+ define_method "_get_cached_#{name}" do
133
+ instance_variable_get("@#{name}") || {}
134
+ end
135
+
136
+ define_method "_set_cached_#{name}" do |value, cache_key|
137
+ cached = send("_get_cached_#{name}")
138
+ cached[cache_key] = value
139
+ instance_variable_set("@#{name}", cached)
140
+ end
141
+
142
+ define_method "_cache_key_for" do |options|
143
+ options.blank? ? :all : options.to_s
144
+ end
145
+ end
146
+
116
147
  class Property
117
148
  attr_reader :name, :options
118
149
 
@@ -127,11 +158,13 @@ module SimplyStored
127
158
 
128
159
  if options[:through]
129
160
  owner_clazz.class_eval do
161
+ define_cache_accessors(name)
130
162
  define_has_many_through_getter(name, options[:through])
131
163
  define_has_many_count(name, options[:through])
132
164
  end
133
165
  else
134
166
  owner_clazz.class_eval do
167
+ define_cache_accessors(name)
135
168
  define_has_many_getter(name)
136
169
  define_has_many_setter_add(name)
137
170
  define_has_many_setter_remove(name)
@@ -50,6 +50,9 @@ module SimplyStored
50
50
  def find(*args)
51
51
  what = args.shift
52
52
  options = args.last.is_a?(Hash) ? args.last : {}
53
+ if options && order = options.delete(:order)
54
+ options[:descending] = true if order == :desc
55
+ end
53
56
 
54
57
  with_deleted = options.delete(:with_deleted)
55
58
 
@@ -121,14 +121,19 @@ module SimplyStored
121
121
  end
122
122
 
123
123
  def find_associated(from, to, options = {})
124
+ view_options = {}
125
+ view_options[:reduce] = false
126
+ view_options[:key] = id
127
+ view_options[:descending] = options[:descending] if options[:descending]
128
+ view_options[:limit] = options[:limit] if options[:limit]
124
129
  if options[:with_deleted]
125
130
  CouchPotato.database.view(
126
131
  self.class.get_class_from_name(from).send(
127
- "association_#{from.to_s.singularize.underscore}_belongs_to_#{to.name.singularize.underscore}_with_deleted", :reduce => false, :key => id))
132
+ "association_#{from.to_s.singularize.underscore}_belongs_to_#{to.name.singularize.underscore}_with_deleted", view_options))
128
133
  else
129
134
  CouchPotato.database.view(
130
135
  self.class.get_class_from_name(from).send(
131
- "association_#{from.to_s.singularize.underscore}_belongs_to_#{to.name.singularize.underscore}", :reduce => false, :key => id))
136
+ "association_#{from.to_s.singularize.underscore}_belongs_to_#{to.name.singularize.underscore}", view_options))
132
137
  end
133
138
  end
134
139
 
@@ -134,11 +134,22 @@ class CouchTest < Test::Unit::TestCase
134
134
 
135
135
  context "when finding instances" do
136
136
  context "with find(:all)" do
137
- should "return all instances" do
137
+ setup do
138
138
  User.create(:title => "Mr.")
139
139
  User.create(:title => "Mrs.")
140
+ end
141
+
142
+ should "return all instances" do
140
143
  assert_equal 2, User.find(:all).size
141
144
  end
145
+
146
+ should "allow a limit" do
147
+ assert_equal 1, User.find(:all, :limit => 1).size
148
+ end
149
+
150
+ should "allow to order the results" do
151
+ assert_equal User.find(:all).reverse, User.find(:all, :order => :desc)
152
+ end
142
153
  end
143
154
 
144
155
  context "to find all instances" do
@@ -159,6 +170,12 @@ class CouchTest < Test::Unit::TestCase
159
170
  assert_equal user, User.first
160
171
  end
161
172
 
173
+ should 'understand the order' do
174
+ assert_nothing_raised do
175
+ User.first(:order => :desc)
176
+ end
177
+ end
178
+
162
179
  should 'return nil when no user found' do
163
180
  assert_nil User.first
164
181
  end
@@ -475,6 +492,72 @@ class CouchTest < Test::Unit::TestCase
475
492
  user.posts
476
493
  end
477
494
 
495
+ context "limit" do
496
+
497
+ should "be able to limit the result set" do
498
+ user = User.create(:title => "Mr.")
499
+ 3.times {
500
+ post = Post.new
501
+ post.user = user
502
+ post.save!
503
+ }
504
+ assert_equal 2, user.posts(:limit => 2).size
505
+ end
506
+
507
+ should "use the given options in the cache-key" do
508
+ user = User.create(:title => "Mr.")
509
+ 3.times {
510
+ post = Post.new
511
+ post.user = user
512
+ post.save!
513
+ }
514
+ assert_equal 2, user.posts(:limit => 2).size
515
+ assert_equal 3, user.posts(:limit => 3).size
516
+ end
517
+
518
+ should "be able to limit the result set - also for through objects" do
519
+ @user = User.create(:title => "Mr.")
520
+ first_pain = Pain.create
521
+ frist_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => first_pain)
522
+ assert_equal [first_pain], @user.pains
523
+ second_pain = Pain.create
524
+ second_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => second_pain)
525
+ @user.reload
526
+ assert_equal 2, @user.pains.size
527
+ assert_equal 1, @user.pains(:limit => 1).size
528
+ end
529
+ end
530
+
531
+ context "order" do
532
+ setup do
533
+ @user = User.create(:title => "Mr.")
534
+ 3.times {
535
+ post = Post.new
536
+ post.user = @user
537
+ post.save!
538
+ }
539
+ end
540
+
541
+ should "support different order" do
542
+ assert_nothing_raised do
543
+ @user.posts(:order => :asc)
544
+ end
545
+
546
+ assert_nothing_raised do
547
+ @user.posts(:order => :desc)
548
+ end
549
+ end
550
+
551
+ should "reverse the order if :desc" do
552
+ assert_equal @user.posts(:order => :asc).map(&:id).reverse, @user.posts(:order => :desc).map(&:id)
553
+ end
554
+
555
+ should "work with the limit option" do
556
+ last_post = Post.create(:user => @user)
557
+ assert_not_equal @user.posts(:order => :asc, :limit => 3).map(&:id).reverse, @user.posts(:order => :desc, :limit => 3).map(&:id)
558
+ end
559
+ end
560
+
478
561
  should "verify the given options for the accessor method" do
479
562
  user = User.create(:title => "Mr.")
480
563
  assert_raise(ArgumentError) do
@@ -509,7 +592,7 @@ class CouchTest < Test::Unit::TestCase
509
592
  post.user = user
510
593
  post.save!
511
594
  user.posts
512
- assert_equal [post], user.instance_variable_get("@posts")
595
+ assert_equal [post], user.instance_variable_get("@posts")[:all]
513
596
  end
514
597
 
515
598
  should "add methods to handle associated objects" do
@@ -535,7 +618,7 @@ class CouchTest < Test::Unit::TestCase
535
618
  assert_equal [], daddy.posts
536
619
  daddy.add_post(item)
537
620
  assert_equal [item], daddy.posts
538
- assert_equal [item], daddy.instance_variable_get("@posts")
621
+ assert_equal [item], daddy.instance_variable_get("@posts")[:all]
539
622
  end
540
623
 
541
624
  should "raise an error when the added item is not an object of the expected class" do
@@ -575,7 +658,7 @@ class CouchTest < Test::Unit::TestCase
575
658
  assert user.posts.include?(post)
576
659
  user.remove_post(post)
577
660
  assert !user.posts.any?{|p| post.id == p.id}
578
- assert_equal [], user.instance_variable_get("@posts")
661
+ assert_equal [], user.instance_variable_get("@posts")[:all]
579
662
  end
580
663
 
581
664
  should "save the removed item with the nullified foreign key" do
@@ -636,7 +719,7 @@ class CouchTest < Test::Unit::TestCase
636
719
  post2 = Post.create(:user => user)
637
720
  user.remove_all_posts
638
721
  assert_equal [], user.posts
639
- assert_equal [], user.instance_variable_get("@posts")
722
+ assert_equal [], user.instance_variable_get("@posts")[:all]
640
723
  end
641
724
 
642
725
  context "when counting" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simply_stored
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mathias Meyer, Jonathan Weiss
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-13 00:00:00 +01:00
12
+ date: 2010-02-18 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency