active_collection 0.2.3 → 0.2.4
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/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
|