friendly_postgres 0.4.3 → 0.4.5

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,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