active_collection 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +1 -1
- data/active_collection.gemspec +10 -4
- data/lib/active_collection/base.rb +16 -17
- data/lib/active_collection/conditions.rb +59 -0
- data/lib/active_collection/includes.rb +6 -6
- data/lib/active_collection/member_class.rb +35 -1
- data/lib/active_collection/new_scope.rb +140 -0
- data/lib/active_collection/order.rb +1 -1
- data/lib/active_collection/pagination.rb +3 -1
- data/lib/active_collection/scope.rb +2 -2
- data/spec/{active_collection_spec.rb → base_spec.rb} +30 -23
- data/spec/member_class_spec.rb +84 -0
- data/spec/pagination_spec.rb +14 -3
- data/spec/proxy_spec.rb +38 -0
- metadata +10 -4
data/VERSION.yml
CHANGED
data/active_collection.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{active_collection}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Martin Emde"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-10-12}
|
13
13
|
s.description = %q{A lazy-loading, Array-like collection proxy for ActiveRecord that understands conditions and paging.}
|
14
14
|
s.email = %q{martin.emde@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,14 +26,18 @@ Gem::Specification.new do |s|
|
|
26
26
|
"active_collection.gemspec",
|
27
27
|
"lib/active_collection.rb",
|
28
28
|
"lib/active_collection/base.rb",
|
29
|
+
"lib/active_collection/conditions.rb",
|
29
30
|
"lib/active_collection/includes.rb",
|
30
31
|
"lib/active_collection/member_class.rb",
|
32
|
+
"lib/active_collection/new_scope.rb",
|
31
33
|
"lib/active_collection/order.rb",
|
32
34
|
"lib/active_collection/pagination.rb",
|
33
35
|
"lib/active_collection/scope.rb",
|
34
36
|
"lib/active_collection/serialization.rb",
|
35
|
-
"spec/
|
37
|
+
"spec/base_spec.rb",
|
38
|
+
"spec/member_class_spec.rb",
|
36
39
|
"spec/pagination_spec.rb",
|
40
|
+
"spec/proxy_spec.rb",
|
37
41
|
"spec/spec.opts",
|
38
42
|
"spec/spec_helper.rb"
|
39
43
|
]
|
@@ -44,8 +48,10 @@ Gem::Specification.new do |s|
|
|
44
48
|
s.rubygems_version = %q{1.3.5}
|
45
49
|
s.summary = %q{A lazy-loading, Array-like collection proxy for ActiveRecord that understands conditions and paging.}
|
46
50
|
s.test_files = [
|
47
|
-
"spec/
|
51
|
+
"spec/base_spec.rb",
|
52
|
+
"spec/member_class_spec.rb",
|
48
53
|
"spec/pagination_spec.rb",
|
54
|
+
"spec/proxy_spec.rb",
|
49
55
|
"spec/spec_helper.rb"
|
50
56
|
]
|
51
57
|
|
@@ -51,16 +51,19 @@ module ActiveCollection
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# dup that doesn't include the collection if it's loaded
|
55
|
+
def unloading_dup
|
56
|
+
d = super
|
57
|
+
d.unload!
|
58
|
+
yield d if block_given?
|
59
|
+
d
|
60
|
+
end
|
61
|
+
|
54
62
|
# Implements Enumerable
|
55
63
|
def each(&block)
|
56
64
|
collection.each(&block)
|
57
65
|
end
|
58
66
|
|
59
|
-
# Grab the raw collection.
|
60
|
-
def all
|
61
|
-
collection
|
62
|
-
end
|
63
|
-
|
64
67
|
# The emptiness of the collection (limited by query and pagination)
|
65
68
|
def empty?
|
66
69
|
size.zero?
|
@@ -69,11 +72,13 @@ module ActiveCollection
|
|
69
72
|
# The size of the collection (limited by query and pagination)
|
70
73
|
#
|
71
74
|
# It will avoid using a count query if the collection is already loaded.
|
75
|
+
#
|
76
|
+
# (Note that the paginated count is added in the pagination module)
|
72
77
|
def size
|
73
|
-
loaded??
|
78
|
+
loaded?? length : total_entries
|
74
79
|
end
|
75
80
|
|
76
|
-
# Always returns the total count
|
81
|
+
# Always returns the total count of all records that can be in this collection.
|
77
82
|
def total_entries
|
78
83
|
@total_entries ||= load_count
|
79
84
|
end
|
@@ -90,6 +95,10 @@ module ActiveCollection
|
|
90
95
|
!!@collection
|
91
96
|
end
|
92
97
|
|
98
|
+
def unload!
|
99
|
+
@collection = nil
|
100
|
+
end
|
101
|
+
|
93
102
|
protected
|
94
103
|
|
95
104
|
# Pass methods on to the collection.
|
@@ -114,16 +123,6 @@ module ActiveCollection
|
|
114
123
|
@collection ||= load_collection
|
115
124
|
end
|
116
125
|
|
117
|
-
# Overload this method to add extra find options.
|
118
|
-
#
|
119
|
-
# :offset and :limit will be overwritten by the pagination_options if the
|
120
|
-
# collection is paginated, because you shouldn't be changing the paging
|
121
|
-
# directly if you're working with a paginated collection
|
122
|
-
#
|
123
|
-
def query_options
|
124
|
-
{}
|
125
|
-
end
|
126
|
-
|
127
126
|
def load_count
|
128
127
|
model_class.count(count_options)
|
129
128
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ActiveCollection
|
2
|
+
module Conditions
|
3
|
+
|
4
|
+
def self.conditiond(mod)
|
5
|
+
mod.extend ClassMethods
|
6
|
+
mod.class_eval do
|
7
|
+
find_scope :conditions_options
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def conditions(*conditions)
|
13
|
+
write_inheritable_attribute(:default_conditions, conditions)
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_conditions
|
17
|
+
read_inheritable_attribute(:default_conditions) ||
|
18
|
+
write_inheritable_attribute(:default_conditions, [])
|
19
|
+
end
|
20
|
+
|
21
|
+
def merge_conditions(a, b)
|
22
|
+
(safe_to_array(a) + safe_to_array(b)).uniq
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def each_condition(&block)
|
27
|
+
@conditions ||= self.class.default_conditions
|
28
|
+
end
|
29
|
+
|
30
|
+
def conditions(*conds)
|
31
|
+
unloading_dup { |ac| ac.condition!(*conds) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def condition!(*new_conditions)
|
35
|
+
raise_if_loaded
|
36
|
+
@conditions = self.class.merge_conditions(new_conditions, conditions).uniq
|
37
|
+
end
|
38
|
+
|
39
|
+
def conditions_options
|
40
|
+
{ :conditions => @conditions } unless @conditions.blank?
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Taken from ActiveRecord::Base
|
46
|
+
#
|
47
|
+
# Object#to_a is deprecated, though it does have the desired behavior
|
48
|
+
def safe_to_array(o)
|
49
|
+
case o
|
50
|
+
when NilClass
|
51
|
+
[]
|
52
|
+
when Array
|
53
|
+
o
|
54
|
+
else
|
55
|
+
[o]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -19,18 +19,18 @@ module ActiveCollection
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def includes
|
22
|
-
@includes
|
22
|
+
@includes ||= self.class.default_includes
|
23
23
|
end
|
24
24
|
|
25
|
-
def include(*
|
26
|
-
ac =
|
27
|
-
ac.include! *
|
25
|
+
def include(*new_includes)
|
26
|
+
ac = unloading_dup
|
27
|
+
ac.include! *new_includes
|
28
28
|
ac
|
29
29
|
end
|
30
30
|
|
31
|
-
def include!(*
|
31
|
+
def include!(*new_includes)
|
32
32
|
raise_if_loaded
|
33
|
-
@includes = (safe_to_array(
|
33
|
+
@includes = (safe_to_array(new_includes) + safe_to_array(includes)).uniq
|
34
34
|
end
|
35
35
|
|
36
36
|
def include_options
|
@@ -5,27 +5,61 @@ module ActiveCollection
|
|
5
5
|
end
|
6
6
|
|
7
7
|
module ClassMethods
|
8
|
+
#
|
9
|
+
# If the name of the class held by your collection cannot be derived from
|
10
|
+
# the name of the collection class (by removing the word Collection from
|
11
|
+
# the end of the collection class name) then use model to set it.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# class WeirdNamedCollection
|
16
|
+
# model "Normal"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# This will use the class Normal to do counts and finds.
|
20
|
+
def model(class_name)
|
21
|
+
(@model_class_name = class_name) && @model_class = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# The actual member class.
|
25
|
+
#
|
26
|
+
# Prints a useful error message if you define your model class wrong.
|
8
27
|
def model_class
|
9
|
-
|
28
|
+
begin
|
29
|
+
@model_class ||= model_class_name.constantize
|
30
|
+
rescue NameError => e
|
31
|
+
raise NameError, %|#{e} - Use 'model "Class"' in the collection to declare the correct model class for #{name}|
|
32
|
+
end
|
10
33
|
end
|
11
34
|
|
35
|
+
# Table name of the member class.
|
12
36
|
def table_name
|
13
37
|
model_class.table_name
|
14
38
|
end
|
15
39
|
|
40
|
+
# Plural human name of the member class.
|
16
41
|
def human_name(*args)
|
17
42
|
model_class.human_name(*args).pluralize
|
18
43
|
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def model_class_name
|
48
|
+
@model_class_name || name.sub(/Collection$/,'')
|
49
|
+
end
|
19
50
|
end
|
20
51
|
|
52
|
+
# The actual member class.
|
21
53
|
def model_class
|
22
54
|
self.class.model_class
|
23
55
|
end
|
24
56
|
|
57
|
+
# Table name of the member class.
|
25
58
|
def table_name
|
26
59
|
self.class.table_name
|
27
60
|
end
|
28
61
|
|
62
|
+
# Plural human name of the member class.
|
29
63
|
def human_name(*args)
|
30
64
|
self.class.human_name(*args)
|
31
65
|
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'active_support/core_ext/array/extract_options'
|
2
|
+
require 'active_support/core_ext/hash/deep_merge'
|
3
|
+
|
4
|
+
module ActiveCollection
|
5
|
+
module Scope
|
6
|
+
#extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def self.included(mod)
|
9
|
+
mod.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Find options for loading the collection.
|
13
|
+
#
|
14
|
+
# To add more options, define a method that returns a hash with the
|
15
|
+
# additional options for find and then add it like this:
|
16
|
+
#
|
17
|
+
# class BeerCollection
|
18
|
+
# find_scope :awesome_beer_only
|
19
|
+
#
|
20
|
+
# def awesome_beer_only
|
21
|
+
# { :conditions => "beer = 'awesome'" }
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
def find_options
|
26
|
+
self.class.scope_for_find.to_options(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Count options for loading the total count.
|
30
|
+
#
|
31
|
+
# To add more options, define a method that returns a hash with the
|
32
|
+
# additional options for count and then add it like this:
|
33
|
+
#
|
34
|
+
# class BeerCollection
|
35
|
+
# count_scope :awesome_beer_only
|
36
|
+
#
|
37
|
+
# def awesome_beer_only
|
38
|
+
# { :conditions => "beer = 'awesome'" }
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
def count_options
|
43
|
+
self.class.scope_for_count.to_options(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def scopes_for_find
|
48
|
+
ScopeBuilder.new(scope_builder + find_scope_builder)
|
49
|
+
end
|
50
|
+
|
51
|
+
def scopes_for_count
|
52
|
+
ScopeBuilder.new(scope_builder + count_scope_builder)
|
53
|
+
end
|
54
|
+
|
55
|
+
[:scope, :find_scope, :count_scope].each do |scope|
|
56
|
+
module_eval <<-SCOPE, __FILE__, __LINE__
|
57
|
+
def #{scope}(*methods, &block)
|
58
|
+
#{scope} = ScopeBuilder.build(:#{scope}, *methods, &block)
|
59
|
+
@#{scope}_builder ||= ScopeBuilder.new
|
60
|
+
@#{scope}_builder.concat #{scope}
|
61
|
+
end
|
62
|
+
|
63
|
+
def #{scope}_builder
|
64
|
+
@#{scope}_builder ||= ScopeBuilder.new
|
65
|
+
if superclass.respond_to?(:#{scope}_builder)
|
66
|
+
superclass.#{scope}_builder + @#{scope}_builder
|
67
|
+
else
|
68
|
+
@#{scope}_builder
|
69
|
+
end
|
70
|
+
end
|
71
|
+
SCOPE
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ScopeBuilder < Array
|
76
|
+
def self.build(kind, *methods, &block)
|
77
|
+
methods, options = extract_options(*methods, &block)
|
78
|
+
methods.map! { |method| ActiveSupport::Callbacks::Callback.new(kind, method, options) }
|
79
|
+
new(methods)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_options(object)
|
83
|
+
inject({}) do |h, callback|
|
84
|
+
res = callback.call(object)
|
85
|
+
res ? h.merge(res) : h
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def join
|
90
|
+
hash = {}
|
91
|
+
each do |scope|
|
92
|
+
next if scope.blank?
|
93
|
+
|
94
|
+
(scope.keys + hash.keys).uniq.each do |key|
|
95
|
+
merge = hash[key] && params[key] # merge if both scopes have the same key
|
96
|
+
|
97
|
+
if key == :conditions && merge
|
98
|
+
hash[key] = if params[key].is_a?(Hash) && hash[key].is_a?(Hash)
|
99
|
+
merge_conditions(hash[key].deep_merge(params[key]))
|
100
|
+
else
|
101
|
+
merge_conditions(params[key], hash[key])
|
102
|
+
end
|
103
|
+
elsif key == :include && merge
|
104
|
+
hash[key] = merge_includes(hash[key], params[key]).uniq
|
105
|
+
elsif key == :joins && merge
|
106
|
+
hash[key] = merge_joins(params[key], hash[key])
|
107
|
+
else
|
108
|
+
hash[key] = hash[key] || params[key]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
hash
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
# Merges conditions so that the result is a valid +condition+
|
118
|
+
def self.merge_conditions(*conditions)
|
119
|
+
segments = []
|
120
|
+
|
121
|
+
conditions.each do |condition|
|
122
|
+
unless condition.blank?
|
123
|
+
sql = model_class.send(:sanitize_sql, condition)
|
124
|
+
segments << sql unless sql.blank?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
"(#{segments.join(') AND (')})" unless segments.empty?
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.extract_options(*methods, &block)
|
132
|
+
methods.flatten!
|
133
|
+
options = methods.extract_options!
|
134
|
+
methods << block if block_given?
|
135
|
+
return methods, options
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
@@ -131,7 +131,7 @@ module ActiveCollection
|
|
131
131
|
|
132
132
|
# Total number of pages.
|
133
133
|
def total_pages
|
134
|
-
|
134
|
+
(total_entries / per_page.to_f).ceil
|
135
135
|
end
|
136
136
|
|
137
137
|
# return a paginated collection if it isn't already paginated.
|
@@ -153,6 +153,7 @@ module ActiveCollection
|
|
153
153
|
current_page && current_page > 0
|
154
154
|
end
|
155
155
|
|
156
|
+
# TODO clean this up
|
156
157
|
def as_data_hash
|
157
158
|
data_hash = { "collection" => collection.as_json }
|
158
159
|
if paginated?
|
@@ -164,6 +165,7 @@ module ActiveCollection
|
|
164
165
|
data_hash
|
165
166
|
end
|
166
167
|
|
168
|
+
# TODO clean this up
|
167
169
|
def to_xml(options = {})
|
168
170
|
collect
|
169
171
|
options[:indent] ||= 2
|
@@ -17,7 +17,7 @@ module ActiveCollection
|
|
17
17
|
# find_scope :awesome_beer_only
|
18
18
|
#
|
19
19
|
# def awesome_beer_only
|
20
|
-
# { :conditions =>
|
20
|
+
# { :conditions => { :beer => 'awesome' } }
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
#
|
@@ -34,7 +34,7 @@ module ActiveCollection
|
|
34
34
|
# count_scope :awesome_beer_only
|
35
35
|
#
|
36
36
|
# def awesome_beer_only
|
37
|
-
# { :conditions =>
|
37
|
+
# { :conditions => { :beer => 'awesome' } }
|
38
38
|
# end
|
39
39
|
# end
|
40
40
|
#
|
@@ -1,21 +1,17 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
class BeerCollection < ActiveCollection::Base
|
4
|
-
end
|
5
|
-
|
6
3
|
class Beer
|
7
4
|
def self.human_name(*args)
|
8
5
|
"Beer"
|
9
6
|
end
|
10
7
|
end
|
11
8
|
|
9
|
+
class BeerCollection < ActiveCollection::Base
|
10
|
+
end
|
11
|
+
|
12
12
|
describe ActiveCollection do
|
13
13
|
subject { BeerCollection.new }
|
14
14
|
|
15
|
-
it "passes human_name to the member class and then pluralizes" do
|
16
|
-
subject.human_name(:locale => 'en-us').should == "Beers"
|
17
|
-
end
|
18
|
-
|
19
15
|
context "(empty)" do
|
20
16
|
describe "(count methods)" do
|
21
17
|
before do
|
@@ -29,6 +25,10 @@ describe ActiveCollection do
|
|
29
25
|
it "has size of 0" do
|
30
26
|
subject.size.should == 0
|
31
27
|
end
|
28
|
+
|
29
|
+
it "has total_entries of 0" do
|
30
|
+
subject.total_entries.should == 0
|
31
|
+
end
|
32
32
|
end
|
33
33
|
|
34
34
|
describe "(collection loading methods)" do
|
@@ -44,11 +44,6 @@ describe ActiveCollection do
|
|
44
44
|
subject.length
|
45
45
|
Beer.should_not_receive(:count)
|
46
46
|
subject.should be_empty
|
47
|
-
end
|
48
|
-
|
49
|
-
it "doesn't load count after loading the collection" do
|
50
|
-
subject.length
|
51
|
-
Beer.should_not_receive(:count)
|
52
47
|
subject.size.should == 0
|
53
48
|
end
|
54
49
|
|
@@ -57,16 +52,16 @@ describe ActiveCollection do
|
|
57
52
|
subject.each { |i| count += 1 }
|
58
53
|
count.should == 0
|
59
54
|
end
|
55
|
+
|
56
|
+
it "returns empty Array on to_a" do
|
57
|
+
subject.to_a.should == []
|
58
|
+
end
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
63
|
-
context "(
|
62
|
+
context "(with 5 records)" do
|
64
63
|
def records
|
65
|
-
@records ||=
|
66
|
-
beers = []
|
67
|
-
5.times { beers << Beer.new }
|
68
|
-
beers
|
69
|
-
end
|
64
|
+
@records ||= (1..5).map { |i| Beer.new }
|
70
65
|
end
|
71
66
|
|
72
67
|
describe "(count methods)" do
|
@@ -98,11 +93,6 @@ describe ActiveCollection do
|
|
98
93
|
subject.length
|
99
94
|
Beer.should_not_receive(:count)
|
100
95
|
subject.should_not be_empty
|
101
|
-
end
|
102
|
-
|
103
|
-
it "doesn't load count after loading the collection" do
|
104
|
-
subject.length
|
105
|
-
Beer.should_not_receive(:count)
|
106
96
|
subject.size.should == 5
|
107
97
|
end
|
108
98
|
|
@@ -111,6 +101,23 @@ describe ActiveCollection do
|
|
111
101
|
subject.each { |i| count += 1 }
|
112
102
|
count.should == 5
|
113
103
|
end
|
104
|
+
|
105
|
+
it "returns all objects in an Array on to_a" do
|
106
|
+
subject.to_a.should == records
|
107
|
+
end
|
108
|
+
|
109
|
+
it "loads the collection on length" do
|
110
|
+
subject.should_not be_loaded
|
111
|
+
subject.length
|
112
|
+
subject.should be_loaded
|
113
|
+
end
|
114
|
+
|
115
|
+
it "unloads a loaded collection on unload!" do
|
116
|
+
subject.to_a
|
117
|
+
subject.should be_loaded
|
118
|
+
subject.unload!
|
119
|
+
subject.should_not be_loaded
|
120
|
+
end
|
114
121
|
end
|
115
122
|
end
|
116
123
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class Beer
|
4
|
+
def self.human_name(*args)
|
5
|
+
"Beer"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.table_name
|
9
|
+
"beers"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class BeerCollection < ActiveCollection::Base
|
14
|
+
end
|
15
|
+
|
16
|
+
class DunkelBeer
|
17
|
+
def self.human_name(*args)
|
18
|
+
"Dunkel Beer"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.table_name
|
22
|
+
"dunkel_beers"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DarkBeerCollection < ActiveCollection::Base
|
27
|
+
model "DunkelBeer"
|
28
|
+
end
|
29
|
+
|
30
|
+
class BrokenCollection < ActiveCollection::Base
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ActiveCollection do
|
34
|
+
context "(with standard name)" do
|
35
|
+
subject { BeerCollection.new }
|
36
|
+
|
37
|
+
it "has the correct model_class" do
|
38
|
+
subject.model_class.should == Beer
|
39
|
+
end
|
40
|
+
|
41
|
+
it "retrieves table_name from member class" do
|
42
|
+
subject.table_name.should == "beers"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "retrieves human_name from member class and pluralizes" do
|
46
|
+
subject.human_name(:locale => 'en-us').should == "Beers"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "(with model)" do
|
51
|
+
subject { DarkBeerCollection.new }
|
52
|
+
|
53
|
+
it "uses the correct model class" do
|
54
|
+
subject.model_class.should == DunkelBeer
|
55
|
+
end
|
56
|
+
|
57
|
+
it "doesn't affect other classes" do
|
58
|
+
BeerCollection.new.model_class.should == Beer
|
59
|
+
end
|
60
|
+
|
61
|
+
it "retrieves table_name from member class" do
|
62
|
+
subject.table_name.should == "dunkel_beers"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "retrieves human_name from member class and pluralizes" do
|
66
|
+
subject.human_name(:locale => 'en-us').should == "Dunkel Beers"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "(broken name)" do
|
71
|
+
subject { BrokenCollection.new }
|
72
|
+
|
73
|
+
it "raises a useful error when usage is attempted" do
|
74
|
+
message = "No exception raised."
|
75
|
+
begin
|
76
|
+
subject.model_class
|
77
|
+
rescue NameError => e
|
78
|
+
message = e.to_s
|
79
|
+
end
|
80
|
+
message.should =~ /Use 'model "Class"' in the collection to declare the correct model class for BrokenCollection/
|
81
|
+
message.should =~ /uninitialized constant Broken/
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/pagination_spec.rb
CHANGED
@@ -52,7 +52,7 @@ describe ActiveCollection do
|
|
52
52
|
|
53
53
|
it_should_behave_like "an empty collection"
|
54
54
|
|
55
|
-
it "
|
55
|
+
it "is out of bounds" do
|
56
56
|
subject.should be_out_of_bounds
|
57
57
|
end
|
58
58
|
|
@@ -70,7 +70,7 @@ describe ActiveCollection do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
context "(
|
73
|
+
context "(with 5 records)" do
|
74
74
|
def records
|
75
75
|
@records ||= begin
|
76
76
|
beers = []
|
@@ -113,6 +113,16 @@ describe ActiveCollection do
|
|
113
113
|
subject.total_entries
|
114
114
|
end
|
115
115
|
|
116
|
+
it "raises on attempt to force paginate after already loaded" do
|
117
|
+
subject.to_a
|
118
|
+
subject.should be_loaded
|
119
|
+
lambda { subject.paginate! }.should raise_error(ActiveCollection::AlreadyLoadedError)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "returns the same record on 'soft' paginate when already paginated" do
|
123
|
+
subject.paginate.should == subject
|
124
|
+
end
|
125
|
+
|
116
126
|
it "yields 5 items to each" do
|
117
127
|
count = 0
|
118
128
|
subject.each { |i| count += 1 }
|
@@ -163,6 +173,7 @@ describe ActiveCollection do
|
|
163
173
|
Beer.should_receive(:all).with(:limit => ActiveCollection::Base.per_page, :offset => 0).and_return(records)
|
164
174
|
subject.length
|
165
175
|
end
|
176
|
+
|
166
177
|
end
|
167
178
|
end
|
168
179
|
|
@@ -230,7 +241,7 @@ describe ActiveCollection do
|
|
230
241
|
end
|
231
242
|
|
232
243
|
it "calls count to find total_entries even when collection is loaded" do
|
233
|
-
subject.
|
244
|
+
subject.to_a
|
234
245
|
Beer.should_receive(:count).and_return(5)
|
235
246
|
subject.total_entries.should == 5
|
236
247
|
end
|
data/spec/proxy_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class Beer
|
4
|
+
def self.human_name(*args)
|
5
|
+
"Beer"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.all(*args)
|
9
|
+
[Beer.new, Beer.new]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class BeerCollection < ActiveCollection::Base
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ActiveCollection, "(proxying)" do
|
17
|
+
subject { BeerCollection.new }
|
18
|
+
|
19
|
+
it "responds to array methods" do
|
20
|
+
subject.should respond_to(:map)
|
21
|
+
subject.should respond_to(:slice)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "loads the collection when sending array methods" do
|
25
|
+
Beer.should_receive(:all).and_return([Beer.new])
|
26
|
+
subject.send(:slice, 0).should_not be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "responds to its own instance methods" do
|
30
|
+
subject.should respond_to(:loaded?)
|
31
|
+
subject.should respond_to(:empty?)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn't load the collection when sending collection methods" do
|
35
|
+
Beer.should_not_receive(:all)
|
36
|
+
subject.send(:loaded?).should be_false
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_collection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Emde
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-10-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -41,14 +41,18 @@ files:
|
|
41
41
|
- active_collection.gemspec
|
42
42
|
- lib/active_collection.rb
|
43
43
|
- lib/active_collection/base.rb
|
44
|
+
- lib/active_collection/conditions.rb
|
44
45
|
- lib/active_collection/includes.rb
|
45
46
|
- lib/active_collection/member_class.rb
|
47
|
+
- lib/active_collection/new_scope.rb
|
46
48
|
- lib/active_collection/order.rb
|
47
49
|
- lib/active_collection/pagination.rb
|
48
50
|
- lib/active_collection/scope.rb
|
49
51
|
- lib/active_collection/serialization.rb
|
50
|
-
- spec/
|
52
|
+
- spec/base_spec.rb
|
53
|
+
- spec/member_class_spec.rb
|
51
54
|
- spec/pagination_spec.rb
|
55
|
+
- spec/proxy_spec.rb
|
52
56
|
- spec/spec.opts
|
53
57
|
- spec/spec_helper.rb
|
54
58
|
has_rdoc: true
|
@@ -80,6 +84,8 @@ signing_key:
|
|
80
84
|
specification_version: 3
|
81
85
|
summary: A lazy-loading, Array-like collection proxy for ActiveRecord that understands conditions and paging.
|
82
86
|
test_files:
|
83
|
-
- spec/
|
87
|
+
- spec/base_spec.rb
|
88
|
+
- spec/member_class_spec.rb
|
84
89
|
- spec/pagination_spec.rb
|
90
|
+
- spec/proxy_spec.rb
|
85
91
|
- spec/spec_helper.rb
|