friendly_postgres 0.4.3 → 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ### (0.4.4)
5
+
6
+ * (jamesgolick) Make it possible to query with order, but no conditions.
7
+ * (jamesgolick) Add change tracking. This is mostly to facilitate arbitrary caches.
8
+
4
9
  ### 0.4.2
5
10
 
6
11
  * (nullstyle) convert UUID to SQL::Blob so that Sequel can properly escape it in databases that don't treat binary strings like regular strings.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.3
1
+ 0.4.5
data/friendly.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{friendly}
8
- s.version = "0.4.3"
8
+ s.version = "0.4.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["James Golick"]
12
- s.date = %q{2009-12-24}
12
+ s.date = %q{2010-01-16}
13
13
  s.description = %q{}
14
14
  s.email = %q{jamesgolick@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -37,13 +37,17 @@ Gem::Specification.new do |s|
37
37
  "lib/friendly/boolean.rb",
38
38
  "lib/friendly/cache.rb",
39
39
  "lib/friendly/cache/by_id.rb",
40
- "lib/friendly/config.rb",
41
40
  "lib/friendly/data_store.rb",
42
41
  "lib/friendly/document.rb",
42
+ "lib/friendly/document/associations.rb",
43
+ "lib/friendly/document/attributes.rb",
44
+ "lib/friendly/document/convenience.rb",
45
+ "lib/friendly/document/mixin.rb",
46
+ "lib/friendly/document/scoping.rb",
47
+ "lib/friendly/document/storage.rb",
43
48
  "lib/friendly/document_table.rb",
44
49
  "lib/friendly/index.rb",
45
50
  "lib/friendly/memcached.rb",
46
- "lib/friendly/named_scope.rb",
47
51
  "lib/friendly/newrelic.rb",
48
52
  "lib/friendly/query.rb",
49
53
  "lib/friendly/scope.rb",
@@ -71,6 +75,7 @@ Gem::Specification.new do |s|
71
75
  "spec/integration/convenience_api_spec.rb",
72
76
  "spec/integration/count_spec.rb",
73
77
  "spec/integration/default_value_spec.rb",
78
+ "spec/integration/dirty_tracking_spec.rb",
74
79
  "spec/integration/find_via_cache_spec.rb",
75
80
  "spec/integration/finder_spec.rb",
76
81
  "spec/integration/has_many_spec.rb",
@@ -87,14 +92,13 @@ Gem::Specification.new do |s|
87
92
  "spec/unit/attribute_spec.rb",
88
93
  "spec/unit/cache_by_id_spec.rb",
89
94
  "spec/unit/cache_spec.rb",
90
- "spec/unit/config_spec.rb",
91
95
  "spec/unit/data_store_spec.rb",
96
+ "spec/unit/document/attributes_spec.rb",
92
97
  "spec/unit/document_spec.rb",
93
98
  "spec/unit/document_table_spec.rb",
94
99
  "spec/unit/friendly_spec.rb",
95
100
  "spec/unit/index_spec.rb",
96
101
  "spec/unit/memcached_spec.rb",
97
- "spec/unit/named_scope_spec.rb",
98
102
  "spec/unit/query_spec.rb",
99
103
  "spec/unit/scope_proxy_spec.rb",
100
104
  "spec/unit/scope_spec.rb",
@@ -164,6 +168,7 @@ Gem::Specification.new do |s|
164
168
  "spec/integration/convenience_api_spec.rb",
165
169
  "spec/integration/count_spec.rb",
166
170
  "spec/integration/default_value_spec.rb",
171
+ "spec/integration/dirty_tracking_spec.rb",
167
172
  "spec/integration/find_via_cache_spec.rb",
168
173
  "spec/integration/finder_spec.rb",
169
174
  "spec/integration/has_many_spec.rb",
@@ -179,14 +184,13 @@ Gem::Specification.new do |s|
179
184
  "spec/unit/attribute_spec.rb",
180
185
  "spec/unit/cache_by_id_spec.rb",
181
186
  "spec/unit/cache_spec.rb",
182
- "spec/unit/config_spec.rb",
183
187
  "spec/unit/data_store_spec.rb",
188
+ "spec/unit/document/attributes_spec.rb",
184
189
  "spec/unit/document_spec.rb",
185
190
  "spec/unit/document_table_spec.rb",
186
191
  "spec/unit/friendly_spec.rb",
187
192
  "spec/unit/index_spec.rb",
188
193
  "spec/unit/memcached_spec.rb",
189
- "spec/unit/named_scope_spec.rb",
190
194
  "spec/unit/query_spec.rb",
191
195
  "spec/unit/scope_proxy_spec.rb",
192
196
  "spec/unit/scope_spec.rb",
@@ -0,0 +1,237 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{friendly_postgres}
8
+ s.version = "0.4.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["James Golick"]
12
+ s.date = %q{2010-01-24}
13
+ s.description = %q{}
14
+ s.email = %q{jamesgolick@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "APACHE-LICENSE",
23
+ "CHANGELOG.md",
24
+ "CONTRIBUTORS.md",
25
+ "LICENSE",
26
+ "README.md",
27
+ "Rakefile",
28
+ "TODO.md",
29
+ "VERSION",
30
+ "examples/friendly.yml",
31
+ "friendly.gemspec",
32
+ "friendly_postgres.gemspec",
33
+ "lib/friendly.rb",
34
+ "lib/friendly/associations.rb",
35
+ "lib/friendly/associations/association.rb",
36
+ "lib/friendly/associations/set.rb",
37
+ "lib/friendly/attribute.rb",
38
+ "lib/friendly/boolean.rb",
39
+ "lib/friendly/cache.rb",
40
+ "lib/friendly/cache/by_id.rb",
41
+ "lib/friendly/data_store.rb",
42
+ "lib/friendly/document.rb",
43
+ "lib/friendly/document/associations.rb",
44
+ "lib/friendly/document/attributes.rb",
45
+ "lib/friendly/document/convenience.rb",
46
+ "lib/friendly/document/mixin.rb",
47
+ "lib/friendly/document/scoping.rb",
48
+ "lib/friendly/document/storage.rb",
49
+ "lib/friendly/document_table.rb",
50
+ "lib/friendly/index.rb",
51
+ "lib/friendly/memcached.rb",
52
+ "lib/friendly/newrelic.rb",
53
+ "lib/friendly/query.rb",
54
+ "lib/friendly/scope.rb",
55
+ "lib/friendly/scope_proxy.rb",
56
+ "lib/friendly/sequel_monkey_patches.rb",
57
+ "lib/friendly/storage.rb",
58
+ "lib/friendly/storage_factory.rb",
59
+ "lib/friendly/storage_proxy.rb",
60
+ "lib/friendly/table.rb",
61
+ "lib/friendly/table_creator.rb",
62
+ "lib/friendly/time.rb",
63
+ "lib/friendly/translator.rb",
64
+ "lib/friendly/uuid.rb",
65
+ "rails/init.rb",
66
+ "spec/config.yml.example",
67
+ "spec/fakes/data_store_fake.rb",
68
+ "spec/fakes/database_fake.rb",
69
+ "spec/fakes/dataset_fake.rb",
70
+ "spec/fakes/document.rb",
71
+ "spec/fakes/serializer_fake.rb",
72
+ "spec/fakes/time_fake.rb",
73
+ "spec/integration/ad_hoc_scopes_spec.rb",
74
+ "spec/integration/basic_object_lifecycle_spec.rb",
75
+ "spec/integration/batch_insertion_spec.rb",
76
+ "spec/integration/convenience_api_spec.rb",
77
+ "spec/integration/count_spec.rb",
78
+ "spec/integration/default_value_spec.rb",
79
+ "spec/integration/dirty_tracking_spec.rb",
80
+ "spec/integration/find_via_cache_spec.rb",
81
+ "spec/integration/finder_spec.rb",
82
+ "spec/integration/has_many_spec.rb",
83
+ "spec/integration/index_spec.rb",
84
+ "spec/integration/named_scope_spec.rb",
85
+ "spec/integration/pagination_spec.rb",
86
+ "spec/integration/scope_chaining_spec.rb",
87
+ "spec/integration/table_creator_spec.rb",
88
+ "spec/integration/write_through_cache_spec.rb",
89
+ "spec/spec.opts",
90
+ "spec/spec_helper.rb",
91
+ "spec/unit/associations/association_spec.rb",
92
+ "spec/unit/associations/set_spec.rb",
93
+ "spec/unit/attribute_spec.rb",
94
+ "spec/unit/cache_by_id_spec.rb",
95
+ "spec/unit/cache_spec.rb",
96
+ "spec/unit/data_store_spec.rb",
97
+ "spec/unit/document/attributes_spec.rb",
98
+ "spec/unit/document_spec.rb",
99
+ "spec/unit/document_table_spec.rb",
100
+ "spec/unit/friendly_spec.rb",
101
+ "spec/unit/index_spec.rb",
102
+ "spec/unit/memcached_spec.rb",
103
+ "spec/unit/query_spec.rb",
104
+ "spec/unit/scope_proxy_spec.rb",
105
+ "spec/unit/scope_spec.rb",
106
+ "spec/unit/storage_factory_spec.rb",
107
+ "spec/unit/storage_proxy_spec.rb",
108
+ "spec/unit/translator_spec.rb",
109
+ "website/index.html",
110
+ "website/scripts/clipboard.swf",
111
+ "website/scripts/shBrushAS3.js",
112
+ "website/scripts/shBrushBash.js",
113
+ "website/scripts/shBrushCSharp.js",
114
+ "website/scripts/shBrushColdFusion.js",
115
+ "website/scripts/shBrushCpp.js",
116
+ "website/scripts/shBrushCss.js",
117
+ "website/scripts/shBrushDelphi.js",
118
+ "website/scripts/shBrushDiff.js",
119
+ "website/scripts/shBrushErlang.js",
120
+ "website/scripts/shBrushGroovy.js",
121
+ "website/scripts/shBrushJScript.js",
122
+ "website/scripts/shBrushJava.js",
123
+ "website/scripts/shBrushJavaFX.js",
124
+ "website/scripts/shBrushPerl.js",
125
+ "website/scripts/shBrushPhp.js",
126
+ "website/scripts/shBrushPlain.js",
127
+ "website/scripts/shBrushPowerShell.js",
128
+ "website/scripts/shBrushPython.js",
129
+ "website/scripts/shBrushRuby.js",
130
+ "website/scripts/shBrushScala.js",
131
+ "website/scripts/shBrushSql.js",
132
+ "website/scripts/shBrushVb.js",
133
+ "website/scripts/shBrushXml.js",
134
+ "website/scripts/shCore.js",
135
+ "website/scripts/shLegacy.js",
136
+ "website/styles/friendly.css",
137
+ "website/styles/help.png",
138
+ "website/styles/ie.css",
139
+ "website/styles/magnifier.png",
140
+ "website/styles/page_white_code.png",
141
+ "website/styles/page_white_copy.png",
142
+ "website/styles/print.css",
143
+ "website/styles/printer.png",
144
+ "website/styles/screen.css",
145
+ "website/styles/shCore.css",
146
+ "website/styles/shThemeDefault.css",
147
+ "website/styles/shThemeDjango.css",
148
+ "website/styles/shThemeEclipse.css",
149
+ "website/styles/shThemeEmacs.css",
150
+ "website/styles/shThemeFadeToGrey.css",
151
+ "website/styles/shThemeMidnight.css",
152
+ "website/styles/shThemeRDark.css"
153
+ ]
154
+ s.homepage = %q{http://friendlyorm.com}
155
+ s.rdoc_options = ["--charset=UTF-8"]
156
+ s.require_paths = ["lib"]
157
+ s.rubygems_version = %q{1.3.5}
158
+ s.summary = %q{NoSQL with MySQL in Ruby}
159
+ s.test_files = [
160
+ "spec/fakes/data_store_fake.rb",
161
+ "spec/fakes/database_fake.rb",
162
+ "spec/fakes/dataset_fake.rb",
163
+ "spec/fakes/document.rb",
164
+ "spec/fakes/serializer_fake.rb",
165
+ "spec/fakes/time_fake.rb",
166
+ "spec/integration/ad_hoc_scopes_spec.rb",
167
+ "spec/integration/basic_object_lifecycle_spec.rb",
168
+ "spec/integration/batch_insertion_spec.rb",
169
+ "spec/integration/convenience_api_spec.rb",
170
+ "spec/integration/count_spec.rb",
171
+ "spec/integration/default_value_spec.rb",
172
+ "spec/integration/dirty_tracking_spec.rb",
173
+ "spec/integration/find_via_cache_spec.rb",
174
+ "spec/integration/finder_spec.rb",
175
+ "spec/integration/has_many_spec.rb",
176
+ "spec/integration/index_spec.rb",
177
+ "spec/integration/named_scope_spec.rb",
178
+ "spec/integration/pagination_spec.rb",
179
+ "spec/integration/scope_chaining_spec.rb",
180
+ "spec/integration/table_creator_spec.rb",
181
+ "spec/integration/write_through_cache_spec.rb",
182
+ "spec/spec_helper.rb",
183
+ "spec/unit/associations/association_spec.rb",
184
+ "spec/unit/associations/set_spec.rb",
185
+ "spec/unit/attribute_spec.rb",
186
+ "spec/unit/cache_by_id_spec.rb",
187
+ "spec/unit/cache_spec.rb",
188
+ "spec/unit/data_store_spec.rb",
189
+ "spec/unit/document/attributes_spec.rb",
190
+ "spec/unit/document_spec.rb",
191
+ "spec/unit/document_table_spec.rb",
192
+ "spec/unit/friendly_spec.rb",
193
+ "spec/unit/index_spec.rb",
194
+ "spec/unit/memcached_spec.rb",
195
+ "spec/unit/query_spec.rb",
196
+ "spec/unit/scope_proxy_spec.rb",
197
+ "spec/unit/scope_spec.rb",
198
+ "spec/unit/storage_factory_spec.rb",
199
+ "spec/unit/storage_proxy_spec.rb",
200
+ "spec/unit/translator_spec.rb"
201
+ ]
202
+
203
+ if s.respond_to? :specification_version then
204
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
205
+ s.specification_version = 3
206
+
207
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
208
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
209
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
210
+ s.add_development_dependency(%q<jferris-mocha>, [">= 0"])
211
+ s.add_development_dependency(%q<memcached>, [">= 0"])
212
+ s.add_runtime_dependency(%q<sequel>, [">= 3.7.0"])
213
+ s.add_runtime_dependency(%q<json_pure>, [">= 0"])
214
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
215
+ s.add_runtime_dependency(%q<will_paginate>, [">= 0"])
216
+ else
217
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
218
+ s.add_dependency(%q<cucumber>, [">= 0"])
219
+ s.add_dependency(%q<jferris-mocha>, [">= 0"])
220
+ s.add_dependency(%q<memcached>, [">= 0"])
221
+ s.add_dependency(%q<sequel>, [">= 3.7.0"])
222
+ s.add_dependency(%q<json_pure>, [">= 0"])
223
+ s.add_dependency(%q<activesupport>, [">= 0"])
224
+ s.add_dependency(%q<will_paginate>, [">= 0"])
225
+ end
226
+ else
227
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
228
+ s.add_dependency(%q<cucumber>, [">= 0"])
229
+ s.add_dependency(%q<jferris-mocha>, [">= 0"])
230
+ s.add_dependency(%q<memcached>, [">= 0"])
231
+ s.add_dependency(%q<sequel>, [">= 3.7.0"])
232
+ s.add_dependency(%q<json_pure>, [">= 0"])
233
+ s.add_dependency(%q<activesupport>, [">= 0"])
234
+ s.add_dependency(%q<will_paginate>, [">= 0"])
235
+ end
236
+ end
237
+
@@ -59,18 +59,25 @@ module Friendly
59
59
  nil
60
60
  end
61
61
  end
62
+
63
+ def assign_default_value(document)
64
+ document.send(:"#{name}=", default)
65
+ end
62
66
 
63
67
  protected
64
68
  def build_accessors
65
69
  n = name
66
70
  klass.class_eval do
71
+ attr_reader n, :"#{n}_was"
72
+
67
73
  eval <<-__END__
68
74
  def #{n}=(value)
75
+ will_change(:#{n})
69
76
  @#{n} = self.class.attributes[:#{n}].typecast(value)
70
77
  end
71
78
 
72
- def #{n}
73
- @#{n} ||= self.class.attributes[:#{n}].default
79
+ def #{n}_changed?
80
+ attribute_changed?(:#{n})
74
81
  end
75
82
  __END__
76
83
  end
@@ -12,7 +12,8 @@ module Friendly
12
12
  end
13
13
 
14
14
  def all(persistable, query)
15
- filtered = dataset(persistable).where(query.conditions)
15
+ filtered = dataset(persistable)
16
+ filtered = filtered.where(query.conditions) unless query.conditions.empty?
16
17
  if query.limit || query.offset
17
18
  filtered = filtered.limit(query.limit, query.offset)
18
19
  end
@@ -0,0 +1,50 @@
1
+ require 'friendly/associations'
2
+ require 'friendly/document/mixin'
3
+
4
+ module Friendly
5
+ module Document
6
+ module Associations
7
+ extend Mixin
8
+
9
+ module ClassMethods
10
+ attr_writer :association_set
11
+
12
+ def association_set
13
+ @association_set ||= Friendly::Associations::Set.new(self)
14
+ end
15
+
16
+ # Add a has_many association.
17
+ #
18
+ # e.g.
19
+ #
20
+ # class Post
21
+ # attribute :user_id, Friendly::UUID
22
+ # indexes :user_id
23
+ # end
24
+ #
25
+ # class User
26
+ # has_many :posts
27
+ # end
28
+ #
29
+ # @user = User.create
30
+ # @post = @user.posts.create
31
+ # @user.posts.all == [@post] # => true
32
+ #
33
+ # _Note: Make sure that the target model is indexed on the foreign key. If it isn't, querying the association will raise Friendly::MissingIndex._
34
+ #
35
+ # Friendly defaults the foreign key to class_name_id just like ActiveRecord.
36
+ # It also converts the name of the association to the name of the target class just like ActiveRecord does.
37
+ #
38
+ # The biggest difference in semantics between Friendly's has_many and active_record's is that Friendly's just returns a Friendly::Scope object. If you want all the associated objects, you have to call #all to get them. You can also use any other Friendly::Scope method.
39
+ #
40
+ # @param [Symbol] name The name of the association and plural name of the target class.
41
+ # @option options [String] :class_name The name of the target class of this association if it is different than the name would imply.
42
+ # @option options [Symbol] :foreign_key Override the foreign key.
43
+ #
44
+ def has_many(name, options = {})
45
+ association_set.add(name, options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,114 @@
1
+ require 'friendly/document/mixin'
2
+ require 'set'
3
+
4
+ module Friendly
5
+ module Document
6
+ module Attributes
7
+ extend Mixin
8
+
9
+ module ClassMethods
10
+ def attribute(name, type = nil, options = {})
11
+ attributes[name] = Attribute.new(self, name, type, options)
12
+ end
13
+
14
+ def attributes
15
+ @attributes ||= {}
16
+ end
17
+
18
+ def new_without_change_tracking(attributes)
19
+ doc = new(attributes)
20
+ doc.reset_changes
21
+ doc
22
+ end
23
+ end
24
+
25
+ def initialize(opts = {})
26
+ assign_default_values
27
+ self.attributes = opts
28
+ end
29
+
30
+ def attributes=(attrs)
31
+ assert_no_duplicate_keys(attrs)
32
+ attrs.each { |name, value| assign(name, value) }
33
+ end
34
+
35
+ def to_hash
36
+ Hash[*self.class.attributes.keys.map { |n| [n, send(n)] }.flatten]
37
+ end
38
+
39
+ def assign_default_values
40
+ self.class.attributes.values.each { |a| a.assign_default_value(self) }
41
+ end
42
+
43
+ def assign(name, value)
44
+ send(:"#{name}=", value)
45
+ end
46
+
47
+ # Notify the object that an attribute is about to change.
48
+ #
49
+ # @param [Symbol] attribute The name of the attribute about to change.
50
+ #
51
+ def will_change(attribute)
52
+ changed << attribute
53
+ instance_variable_set(:"@#{attribute}_was", send(attribute))
54
+ end
55
+
56
+ # Get the original value of an attribute that has changed.
57
+ #
58
+ # @param [Symbol] attribute The name of the attribute.
59
+ #
60
+ def attribute_was(attribute)
61
+ instance_variable_get(:"@#{attribute}_was")
62
+ end
63
+
64
+ # Has this attribute changed?
65
+ #
66
+ # @param [Symbol] attribute The name of the attribute.
67
+ #
68
+ def attribute_changed?(attribute)
69
+ changed.include?(attribute)
70
+ end
71
+
72
+ # Have any of the attributes that are being tracked changed since last reset?
73
+ #
74
+ def changed?
75
+ !changed.empty?
76
+ end
77
+
78
+ # Which attributes that are being tracked have changed since last reset?
79
+ #
80
+ def changed
81
+ @changed ||= Set.new
82
+ end
83
+
84
+ # Reset all the changes to this object.
85
+ #
86
+ def reset_changes
87
+ changed.each { |c| not_changed(c) }.clear
88
+ end
89
+
90
+ # Reset the changed-ness of one attribute.
91
+ #
92
+ def not_changed(attribute)
93
+ instance_variable_set(:"@#{attribute}_was", nil)
94
+ changed.delete(attribute)
95
+ end
96
+
97
+ # Override #save to reset changes afterwards
98
+ #
99
+ # @override
100
+ #
101
+ def save
102
+ super
103
+ reset_changes
104
+ end
105
+
106
+ protected
107
+ def assert_no_duplicate_keys(hash)
108
+ if hash.keys.map { |k| k.to_s }.uniq.length < hash.keys.length
109
+ raise ArgumentError, "Duplicate keys: #{hash.inspect}"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,41 @@
1
+ require 'friendly/document/mixin'
2
+
3
+ module Friendly
4
+ module Document
5
+ module Convenience
6
+ extend Mixin
7
+
8
+ module ClassMethods
9
+ attr_writer :collection_klass
10
+
11
+ def collection_klass
12
+ @collection_klass ||= WillPaginate::Collection
13
+ end
14
+
15
+ def find(id)
16
+ doc = first(:id => id)
17
+ raise RecordNotFound, "Couldn't find #{name}/#{id}" if doc.nil?
18
+ doc
19
+ end
20
+
21
+ def paginate(conditions)
22
+ query = query(conditions)
23
+ count = count(query)
24
+ collection = collection_klass.new(query.page, query.per_page, count)
25
+ collection.replace(all(query))
26
+ end
27
+
28
+ def create(attributes = {})
29
+ doc = new(attributes)
30
+ doc.save
31
+ doc
32
+ end
33
+ end
34
+
35
+ def update_attributes(attributes)
36
+ self.attributes = attributes
37
+ save
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ module Friendly
2
+ module Document
3
+ module Mixin
4
+ # FIXME: I'm not in love with this. But, I also don't think it's the
5
+ # end of the world.
6
+ def included(klass)
7
+ if klass.const_defined?(:ClassMethods)
8
+ klass.const_get(:ClassMethods).send(:include, const_get(:ClassMethods))
9
+ else
10
+ klass.send(:extend, const_get(:ClassMethods))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,66 @@
1
+ require 'friendly/document/mixin'
2
+
3
+ module Friendly
4
+ module Document
5
+ module Scoping
6
+ extend Mixin
7
+
8
+ module ClassMethods
9
+ attr_writer :scope_proxy
10
+
11
+ def scope_proxy
12
+ @scope_proxy ||= ScopeProxy.new(self)
13
+ end
14
+
15
+ # Add a named scope to this Document.
16
+ #
17
+ # e.g.
18
+ #
19
+ # class Post
20
+ # indexes :created_at
21
+ # named_scope :recent, :order! => :created_at.desc
22
+ # end
23
+ #
24
+ # Then, you can access the recent posts with:
25
+ #
26
+ # Post.recent.all
27
+ # ...or...
28
+ # Post.recent.first
29
+ #
30
+ # Both #all and #first also take additional parameters:
31
+ #
32
+ # Post.recent.all(:author_id => @author.id)
33
+ #
34
+ # Scopes are also chainable. See the README or Friendly::Scope docs for details.
35
+ #
36
+ # @param [Symbol] name the name of the scope.
37
+ # @param [Hash] parameters the query that this named scope will perform.
38
+ #
39
+ def named_scope(name, parameters)
40
+ scope_proxy.add_named(name, parameters)
41
+ end
42
+
43
+ # Returns boolean based on whether the Document has a scope by a particular name.
44
+ #
45
+ # @param [Symbol] name The name of the scope in question.
46
+ #
47
+ def has_named_scope?(name)
48
+ scope_proxy.has_named_scope?(name)
49
+ end
50
+
51
+ # Create an ad hoc scope on this Document.
52
+ #
53
+ # e.g.
54
+ #
55
+ # scope = Post.scope(:order! => :created_at)
56
+ # scope.all # => [#<Post>, #<Post>]
57
+ #
58
+ # @param [Hash] parameters the query parameters to create the scope with.
59
+ #
60
+ def scope(parameters)
61
+ scope_proxy.ad_hoc(parameters)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end