mongomapper_ext 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 David A. Cuadrado
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = mongomapper_ext
2
+
3
+ MongoMapper extensions.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 David A. Cuadrado. See LICENSE for details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "mongomapper_ext"
8
+ gem.summary = %Q{MongoMapper extensions}
9
+ gem.description = %Q{MongoMapper extensions}
10
+ gem.email = "krawek@gmail.com"
11
+ gem.homepage = "http://github.com/dcu/mongomapper_ext"
12
+ gem.authors = ["David A. Cuadrado"]
13
+
14
+ gem.add_dependency('mongo_mapper', '>= 0.5.6')
15
+ gem.add_dependency('ruby-stemmer', '>= 0.5.3')
16
+ gem.add_dependency('uuidtools', '>= 2.0.0')
17
+
18
+ gem.add_development_dependency("shoulda", ">= 2.10.2")
19
+ gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
20
+ gem.add_development_dependency('mocha', '>= 0.9.4')
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "mongomapper_ext #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
59
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
@@ -0,0 +1,23 @@
1
+ require './helper'
2
+
3
+ class FilterEx
4
+ include MongoMapper::Document
5
+ include MongoMapperExt::Filter
6
+
7
+ key :title, String
8
+ key :body, String
9
+
10
+ filterable_keys :title, :body
11
+ end
12
+
13
+ FilterEx.delete_all
14
+ o1 = FilterEx.create(:title => "A Great Title", :body => "A great Body")
15
+ o2 = FilterEx.create(:title => "A Good Title", :body => "A good Body")
16
+
17
+
18
+ puts "filter: #{FilterEx.filter("title").size}"
19
+ puts "filter: #{FilterEx.filter("title", :limit => 1).inspect}"
20
+ puts "filter: #{FilterEx.filter("great").inspect}"
21
+
22
+
23
+ FilterEx.delete_all
@@ -0,0 +1,6 @@
1
+
2
+ $:.unshift File.dirname(__FILE__)+"/../lib"
3
+ require 'mongomapper_ext'
4
+
5
+ MongoMapper.database = 'test'
6
+
@@ -0,0 +1,15 @@
1
+ require './helper'
2
+
3
+ class SlugizerEx
4
+ include MongoMapper::Document
5
+ include MongoMapperExt::Slugizer
6
+
7
+ slug_key :title
8
+
9
+ key :title
10
+ end
11
+
12
+ u = SlugizerEx.create(:title => "The title of the blogpost!!!")
13
+
14
+ puts u.slug
15
+ puts SlugizerEx.by_slug(u.slug).inspect
@@ -0,0 +1,22 @@
1
+ require './helper'
2
+
3
+ class StorageEx
4
+ include MongoMapper::Document
5
+ include MongoMapperExt::Storage
6
+
7
+ file_key :my_file
8
+ end
9
+
10
+ file = StringIO.new("file content")
11
+ file2 = StringIO.new("file content 2")
12
+
13
+ s = StorageEx.new
14
+ s.put_file("filename.txt", file)
15
+ s.my_file = file2
16
+
17
+ s.save
18
+
19
+ from_db = StorageEx.find(s.id)
20
+
21
+ puts from_db.fetch_file("filename.txt").read
22
+ puts from_db.my_file.read
data/examples/types.rb ADDED
@@ -0,0 +1,32 @@
1
+ require './helper'
2
+
3
+ class TypeEx
4
+ include MongoMapper::Document
5
+
6
+ key :time, Timestamp
7
+ key :config, OpenStruct
8
+ key :set, Set
9
+ end
10
+
11
+
12
+ type = TypeEx.new(:set => Set.new, :config => OpenStruct.new)
13
+
14
+ type.config.name = "Alan"
15
+ type.config.last_name = "Turing"
16
+
17
+
18
+ type.time = Time.now
19
+ type.set += [1,1,2,3,2,2]
20
+
21
+ type.save
22
+
23
+ from_db = TypeEx.find(type.id)
24
+
25
+ puts from_db.config.inspect
26
+ puts from_db.set.inspect
27
+
28
+ Time.zone = "Hawaii"
29
+ puts from_db.time.inspect
30
+
31
+ from_db.destroy
32
+
@@ -0,0 +1,14 @@
1
+ require './helper'
2
+
3
+ class UpdateEx
4
+ include MongoMapper::Document
5
+
6
+ key :a
7
+ key :b
8
+ key :c
9
+ end
10
+
11
+ u = UpdateEx.create(:a => 1, :b => 0, :c => 1)
12
+
13
+ u.safe_update(%w[a c], {"a" => 3, "b" => 3, "c" => 3})
14
+ puts u.attributes.inspect
@@ -0,0 +1,35 @@
1
+ module MongoMapperExt
2
+ class File < GridFS::GridStore
3
+ attr_reader :id, :attributes
4
+
5
+ def initialize(owner, attrs = {})
6
+ @owner = owner
7
+ @id = attrs.delete("_id")
8
+
9
+ class_eval do
10
+ attrs.each do |k,v|
11
+ define_method(k) do
12
+ v
13
+ end
14
+ end
15
+ end
16
+
17
+ super(@owner.class.database, attrs["filename"], "r", :root => @owner.collection.name)
18
+ end
19
+
20
+ def [](name)
21
+ @attributes[name.to_s]
22
+ end
23
+
24
+ def self.fetch(owner, filename)
25
+ db = owner.class.database
26
+ criteria, options = MongoMapper::FinderOptions.new(owner.class, :filename => filename, :metadata => {:_id => owner.id}, :limit => 1).to_a
27
+
28
+ obj = db.collection("#{owner.collection.name}.files").find(criteria, options).next_object
29
+
30
+ if obj
31
+ self.new(owner, obj)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,76 @@
1
+ module MongoMapperExt
2
+ module Filter
3
+ def self.included(klass)
4
+ require 'lingua/stemmer'
5
+
6
+ klass.class_eval do
7
+ extend ClassMethods
8
+
9
+ key :_keywords, Array
10
+ ensure_index :_keywords
11
+
12
+ before_save :_update_keywords
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def filterable_keys(*keys)
18
+ @filterable_keys ||= Set.new
19
+ @filterable_keys += keys
20
+
21
+ @filterable_keys
22
+ end
23
+
24
+ def language(lang = 'en')
25
+ @language ||= lang
26
+ end
27
+
28
+ def filter(query, opts = {})
29
+ q = query.downcase.split.map do |k|
30
+ Regexp.escape(k)
31
+ end.join("|")
32
+ if opts[:per_page]
33
+ self.paginate(opts.deep_merge(:conditions => {:_keywords => /^(#{q}).*/ }))
34
+ else
35
+ self.find(:all, opts.deep_merge(:conditions => {:_keywords => /^(#{q}).*/ }))
36
+ end
37
+ end
38
+ end
39
+
40
+ protected
41
+ def _update_keywords
42
+ lang = self.class.language
43
+ if lang.kind_of?(Symbol)
44
+ lang = send(lang)
45
+ elsif lang.kind_of?(Proc)
46
+ lang = lang.call(self)
47
+ end
48
+
49
+ s = Lingua::Stemmer.new(:language => lang)
50
+
51
+ self._keywords = []
52
+ self.class.filterable_keys.each do |key|
53
+ self._keywords += keywords_for_value(s, read_attribute(key))
54
+ end
55
+ end
56
+
57
+ private
58
+ def keywords_for_value(stemmer, val)
59
+ if val.kind_of?(String)
60
+ val.downcase.split.map do |word|
61
+ stem = stemmer.stem(word)
62
+ if stem != word
63
+ [stem, word]
64
+ else
65
+ word
66
+ end
67
+ end.flatten
68
+ elsif val.kind_of?(Array)
69
+ val.map { |e| keywords_for_value(stemmer, e) }.flatten
70
+ else
71
+ [val]
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,46 @@
1
+ module MongoMapperExt
2
+ module Slugizer
3
+ def self.included(klass)
4
+ klass.class_eval do
5
+ extend ClassMethods
6
+ extend Finder
7
+
8
+ key :slug, String
9
+ ensure_index :slug
10
+
11
+ before_validation_on_create :generate_slug
12
+ end
13
+ end
14
+
15
+ def to_param
16
+ if self.slug.blank?
17
+ self.id
18
+ else
19
+ self.slug
20
+ end
21
+ end
22
+
23
+ protected
24
+ def generate_slug
25
+ if self.slug.blank?
26
+ key = UUIDTools::UUID.random_create.hexdigest[0,4] #optimize
27
+ self.slug = key+"-"+self[self.class.slug_key].gsub(/[^A-Za-z0-9\s\-]/, "")[0,20].strip.gsub(/\s+/, "-").downcase
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ def slug_key(key = :name)
33
+ @slug_key ||= key
34
+ end
35
+ end
36
+
37
+ module Finder
38
+ def by_slug(id)
39
+ self.find_by_slug(id) || self.find_by_id(id)
40
+ end
41
+ alias :find_by_slug_or_id :by_slug
42
+ end
43
+ end
44
+ end
45
+
46
+ MongoMapper::Associations::Proxy.send(:include, MongoMapperExt::Slugizer::Finder)
@@ -0,0 +1,73 @@
1
+ module MongoMapperExt
2
+ module Storage
3
+ def self.included(model)
4
+ model.class_eval do
5
+ extend ClassMethods
6
+ after_create :_sync_pending_files
7
+ end
8
+ end
9
+
10
+ # FIXME: enable metadata. re http://jira.mongodb.org/browse/SERVER-377
11
+ def put_file(filename, io, metadata = {})
12
+ if !new?
13
+ # :metadata => metadata.deep_merge({:_id => self.id})
14
+ GridFS::GridStore.open(self.class.database, filename, "w",
15
+ :root => self.collection.name,
16
+ :metadata => {:_id => self.id}) do |f|
17
+ while data = io.read(256)
18
+ f.write(data)
19
+ end
20
+ io.close
21
+ end
22
+ else
23
+ (@_pending_files ||= {})[filename] = io
24
+ end
25
+ end
26
+
27
+ def fetch_file(filename)
28
+ if !new?
29
+ MongoMapperExt::File.fetch(self, filename)
30
+ end
31
+ end
32
+
33
+ def files
34
+ criteria, options = MongoMapper::FinderOptions.new(self.class, :metadata => {:_id => self.id}).to_a
35
+ coll = self.class.database.collection("#{self.collection.name}.files")
36
+ @files = coll.find(criteria, options).map do |a|
37
+ MongoMapperExt::File.new(self, a)
38
+ end
39
+ end
40
+
41
+ protected
42
+ def _sync_pending_files
43
+ if @_pending_files
44
+ @_pending_files.each do |filename, data|
45
+ put_file(filename, data)
46
+ end
47
+ @_pending_files = nil
48
+ end
49
+ end
50
+
51
+ module ClassMethods
52
+ def file_key(name)
53
+ define_method("#{name}=") do |file|
54
+ file_id = UUIDTools::UUID.random_create.hexdigest
55
+ filename = name
56
+
57
+ if file.respond_to?(:original_filename)
58
+ filename = file.original_filename
59
+ elsif file.respond_to?(:path)
60
+ filename = file.path
61
+ end
62
+
63
+ put_file(file_id, file, :original_filename => filename)
64
+ self["_#{name}"] = file_id
65
+ end
66
+
67
+ define_method(name) do
68
+ fetch_file(self["_#{name}"])
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,19 @@
1
+ require 'ostruct'
2
+
3
+ class OpenStruct
4
+ def self.to_mongo(value)
5
+ if value.kind_of?(self)
6
+ value.send(:table)
7
+ else
8
+ value
9
+ end
10
+ end
11
+
12
+ def self.from_mongo(value)
13
+ if value.kind_of?(self)
14
+ value
15
+ else
16
+ OpenStruct.new(value || {})
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ class Set
2
+ def self.to_mongo(value)
3
+ value.to_a
4
+ end
5
+
6
+ def self.from_mongo(value)
7
+ Set.new(value)
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class Timestamp
2
+ def self.from_mongo(value)
3
+ if value.nil? || value == ''
4
+ nil
5
+ else
6
+ Time.zone.at(value.to_i)
7
+ end
8
+ end
9
+
10
+ def self.to_mongo(value)
11
+ value.to_i
12
+ end
13
+ end
14
+
15
+ Time.zone ||= 'UTC'
@@ -0,0 +1,10 @@
1
+ module MongoMapperExt
2
+ module Update
3
+ def safe_update(white_list, values)
4
+ white_list.each do |key|
5
+ send("#{key}=", values[key]) if values.has_key?(key)
6
+ end
7
+ end
8
+ end
9
+ end
10
+ MongoMapper::EmbeddedDocument.send(:include, MongoMapperExt::Update)
@@ -0,0 +1,23 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'mongo/gridfs'
4
+ require 'mongo_mapper'
5
+ require 'uuidtools'
6
+
7
+ # types
8
+ require 'mongomapper_ext/types/open_struct'
9
+ require 'mongomapper_ext/types/timestamp'
10
+ require 'mongomapper_ext/types/set'
11
+
12
+ # storage
13
+ require 'mongomapper_ext/file'
14
+ require 'mongomapper_ext/storage'
15
+
16
+ # update
17
+ require 'mongomapper_ext/update'
18
+
19
+ # filter
20
+ require 'mongomapper_ext/filter'
21
+
22
+ # slug
23
+ require 'mongomapper_ext/slugizer'
data/test/helper.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+
3
+ gem 'jnunemaker-matchy'
4
+
5
+ require 'matchy'
6
+ require 'shoulda'
7
+ require 'timecop'
8
+ require 'mocha'
9
+ require 'pp'
10
+
11
+ require 'support/custom_matchers'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+
16
+ require 'mongomapper_ext'
17
+ require 'models'
18
+
19
+ class Test::Unit::TestCase
20
+ include CustomMatchers
21
+ end
22
+
23
+ MongoMapper.database = 'test'
data/test/models.rb ADDED
@@ -0,0 +1,52 @@
1
+
2
+ class Event # for safe_update, and Timestamp
3
+ include MongoMapper::Document
4
+
5
+ key :start_date, Timestamp
6
+ key :end_date, Timestamp
7
+
8
+ key :password, String
9
+ end
10
+
11
+ class Recipe # for Set
12
+ include MongoMapper::Document
13
+ include MongoMapperExt::Filter
14
+
15
+ language Proc.new { |d| d.language }
16
+ filterable_keys :language
17
+
18
+ key :ingredients, Set
19
+ key :description, String
20
+ key :language, String, :default => 'en'
21
+ end
22
+
23
+ class Avatar # for Storage and File
24
+ include MongoMapper::Document
25
+ include MongoMapperExt::Storage
26
+
27
+ file_key :data
28
+ end
29
+
30
+ class UserConfig #for OpenStruct
31
+ include MongoMapper::Document
32
+ key :entries, OpenStruct
33
+ end
34
+
35
+ class BlogPost # for Slug and Filter
36
+ include MongoMapper::Document
37
+ include MongoMapperExt::Filter
38
+ include MongoMapperExt::Slugizer
39
+
40
+ filterable_keys :title, :body, :tags, :date
41
+ slug_key :title
42
+ language :find_language
43
+
44
+ key :title, String
45
+ key :body, String
46
+ key :tags, Array
47
+ key :date, Time
48
+
49
+ def find_language
50
+ 'en'
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ module CustomMatchers
2
+ custom_matcher :be_nil do |receiver, matcher, args|
3
+ matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
4
+ matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
5
+ receiver.nil?
6
+ end
7
+
8
+ custom_matcher :be_blank do |receiver, matcher, args|
9
+ matcher.positive_failure_message = "Expected #{receiver} to be blank but it wasn't"
10
+ matcher.negative_failure_message = "Expected #{receiver} not to be blank but it was"
11
+ receiver.blank?
12
+ end
13
+
14
+ custom_matcher :be_true do |receiver, matcher, args|
15
+ matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
16
+ matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
17
+ receiver.eql?(true)
18
+ end
19
+
20
+ custom_matcher :be_false do |receiver, matcher, args|
21
+ matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
22
+ matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
23
+ receiver.eql?(false)
24
+ end
25
+
26
+ custom_matcher :be_valid do |receiver, matcher, args|
27
+ matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
28
+ matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
29
+ receiver.valid?
30
+ end
31
+
32
+ custom_matcher :have_error_on do |receiver, matcher, args|
33
+ receiver.valid?
34
+ attribute = args[0]
35
+ expected_message = args[1]
36
+
37
+ if expected_message.nil?
38
+ matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
39
+ matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
40
+ !receiver.errors.on(attribute).blank?
41
+ else
42
+ actual = receiver.errors.on(attribute)
43
+ matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
44
+ matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
45
+ actual == expected_message
46
+ end
47
+ end
48
+
49
+ custom_matcher :have_index do |receiver, matcher, args|
50
+ index_name = args[0]
51
+ matcher.positive_failure_message = "#{receiver} does not have index named #{index_name}, but should"
52
+ matcher.negative_failure_message = "#{receiver} does have index named #{index_name}, but should not"
53
+ !receiver.collection.index_information.detect { |index| index[0] == index_name }.nil?
54
+ end
55
+ end
@@ -0,0 +1,43 @@
1
+ require 'helper'
2
+
3
+ class TestFilter < Test::Unit::TestCase
4
+ context "filtering data" do
5
+ setup do
6
+ BlogPost.delete_all
7
+ @blogpost = BlogPost.create(:title => "%How dOEs tHIs Work?!",
8
+ :body => "HeRe is tHe Body of the bLog pOsT",
9
+ :tags => ["my", "list", "of", "tags"],
10
+ :date => Time.parse('01-01-2009'))
11
+ end
12
+
13
+ should "be case insensitive" do
14
+ BlogPost.filter("body").should == [@blogpost]
15
+ end
16
+
17
+ should "be able to find by title" do
18
+ BlogPost.filter("this").should == [@blogpost]
19
+ end
20
+
21
+ should "be able to find by body" do
22
+ BlogPost.filter("blog").should == [@blogpost]
23
+ end
24
+
25
+ should "be able to find by tags" do
26
+ BlogPost.filter("list").should == [@blogpost]
27
+ end
28
+
29
+ should "be able to find by title or body" do
30
+ BlogPost.filter("work blog").should == [@blogpost]
31
+ end
32
+
33
+ should "ignore inexistant words" do
34
+ BlogPost.filter("work lalala").should == [@blogpost]
35
+ end
36
+
37
+ should "allow to paginate results" do
38
+ results = BlogPost.filter("tag", :per_page => 1, :page => 1)
39
+ results.should == [@blogpost]
40
+ results.total_pages.should == 1
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ require 'helper'
2
+
3
+ class TestSlugizer < Test::Unit::TestCase
4
+ context "working with slugs" do
5
+ setup do
6
+ BlogPost.delete_all
7
+ @blogpost = BlogPost.create(:title => "%bLog pOSt tiTLe!",
8
+ :body => "HeRe is tHe Body of the bLog pOsT")
9
+ end
10
+
11
+ should "generate the slug" do
12
+ @blogpost.slug.should =~ /\w+-blog-post-title/
13
+ end
14
+
15
+ should "return the slug as param" do
16
+ @blogpost.to_param =~ /\w+-blog-post-title/
17
+ end
18
+
19
+ should "return the id if slug was not generated" do
20
+ @blogpost.slug = nil
21
+ @blogpost.to_param.should == @blogpost.id
22
+ end
23
+ end
24
+
25
+ context "finding objects" do
26
+ setup do
27
+ BlogPost.delete_all
28
+ @blogpost = BlogPost.create(:title => "%bLog pOSt tiTLe!",
29
+ :body => "HeRe is tHe Body of the bLog pOsT")
30
+ end
31
+
32
+ should "be able to find by slug" do
33
+ BlogPost.by_slug(@blogpost.slug).should == @blogpost
34
+ end
35
+
36
+ should "be able to find by id" do
37
+ BlogPost.by_slug(@blogpost.id).should == @blogpost
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+
3
+ class StorageTest < Test::Unit::TestCase
4
+ context "Storing files" do
5
+ setup do
6
+ @avatar = Avatar.create
7
+ @data = StringIO.new("my avatar image")
8
+ end
9
+
10
+ should "store the file" do
11
+ @avatar.put_file("an_avatar.png", @data)
12
+ data = Avatar.find(@avatar.id).fetch_file("an_avatar.png").read
13
+ data.should == "my avatar image"
14
+ end
15
+
16
+ should "close the file after storing" do
17
+ @avatar.put_file("an_avatar.png", @data)
18
+ @data.should be_closed
19
+ end
20
+
21
+ context "in attributes" do
22
+ should "store the given file" do
23
+ @avatar.data = @data
24
+ @avatar.save!
25
+ @avatar.data.should_not be_nil
26
+ @avatar.data.read.should == "my avatar image"
27
+ end
28
+ end
29
+
30
+ context "with new objects" do
31
+ setup do
32
+ @avatar = Avatar.new
33
+ end
34
+
35
+ should "store the file after saving" do
36
+ @avatar.put_file("an_avatar.png", @data)
37
+ @avatar.save
38
+ @avatar.fetch_file("an_avatar.png").read.should == "my avatar image"
39
+ end
40
+
41
+ should "store not the file if object is new" do
42
+ @avatar.put_file("an_avatar.png", @data)
43
+ @avatar.fetch_file("an_avatar.png").should be_nil
44
+ end
45
+ end
46
+ end
47
+
48
+ context "Fetching files" do
49
+ setup do
50
+ @avatar = Avatar.create
51
+ @data = StringIO.new("my avatar image")
52
+ end
53
+
54
+ should "fetch the list of files" do
55
+ @avatar.put_file("file1", StringIO.new("data1"))
56
+ @avatar.put_file("file2", StringIO.new("data2"))
57
+ @avatar.put_file("file3", StringIO.new("data3"))
58
+ file_names = @avatar.files.map { |f| f.filename }
59
+ file_names.should include("file1")
60
+ file_names.should include("file2")
61
+ file_names.should include("file3")
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,16 @@
1
+ require 'helper'
2
+
3
+ class UpdateTest < Test::Unit::TestCase
4
+ should "only update the given white listed attributes" do
5
+ event = Event.new(:password => "original")
6
+ start_date = Time.zone.now
7
+ end_date = start_date.tomorrow
8
+
9
+ event.safe_update(%w[start_date end_date], {"start_date" => start_date,
10
+ "end_date" => end_date,
11
+ "password" => "hacked"})
12
+ event.password.should == "original"
13
+ event.start_date.to_s.should == start_date.to_s
14
+ event.end_date.to_s.should == end_date.to_s
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ class OpenStructTest < Test::Unit::TestCase
4
+ def from_db
5
+ UserConfig.find(@config.id)
6
+ end
7
+
8
+ context "working with sets" do
9
+ setup do
10
+ @config = UserConfig.create!(:entries => OpenStruct.new({}))
11
+ end
12
+
13
+ should "allow to add new keys" do
14
+ @config.entries.new_key = "my new key"
15
+ @config.save!
16
+ from_db.entries.new_key.should == "my new key"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ require 'helper'
2
+
3
+ class SetTest < Test::Unit::TestCase
4
+ def from_db
5
+ Recipe.find(@recipe.id)
6
+ end
7
+
8
+ context "working with sets" do
9
+ setup do
10
+ @recipe = Recipe.create!(:ingredients => %w[salt sugar water salt sugar water])
11
+ end
12
+
13
+ should "not have duplicates" do
14
+ from_db.ingredients.size.should == 3
15
+ from_db.ingredients.should include("salt")
16
+ from_db.ingredients.should include("sugar")
17
+ from_db.ingredients.should include("water")
18
+ end
19
+
20
+ should "not add duplicates" do
21
+ original_size = @recipe.ingredients.size
22
+ @recipe.ingredients << "salt"
23
+ @recipe.ingredients.size.should == original_size
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'helper'
2
+
3
+ class TimestampTest < Test::Unit::TestCase
4
+ def from_db
5
+ Event.find(@event.id)
6
+ end
7
+
8
+ context "working with timestamps" do
9
+ setup do
10
+ Event.delete_all
11
+
12
+ Time.zone = 'UTC'
13
+ @start_time = Time.zone.parse('01-01-2009')
14
+ @end_time = @start_time.tomorrow
15
+
16
+ @event = Event.create!(:start_date => @start_time, :end_date => @end_time)
17
+ end
18
+
19
+ should "store the date" do
20
+ from_db.start_date.to_s.should == @start_time.to_s
21
+ end
22
+
23
+ should "be able to convert the time to the given timezone" do
24
+ Time.zone = 'Hawaii'
25
+ from_db.start_date.to_s.should == "2008-12-31 14:00:00 -1000"
26
+ end
27
+
28
+ should "be able to compare dates" do
29
+ start_time = @start_time.tomorrow.tomorrow
30
+ end_time = start_time.tomorrow
31
+
32
+ @event2 = Event.create!(:start_date => start_time, :end_datime => end_time)
33
+
34
+ Event.count.should == 2
35
+ events = Event.find(:all, :$where => ("this.start_date >= %d && this.start_date <= %d" % [@event.start_date.yesterday.to_i, @event2.start_date.yesterday.to_i]))
36
+
37
+ events.should == [@event]
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongomapper_ext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - David A. Cuadrado
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-25 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mongo_mapper
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.6
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-stemmer
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.3
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: uuidtools
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.0
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: shoulda
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.10.2
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: jnunemaker-matchy
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.4.0
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: mocha
67
+ type: :development
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.9.4
74
+ version:
75
+ description: MongoMapper extensions
76
+ email: krawek@gmail.com
77
+ executables: []
78
+
79
+ extensions: []
80
+
81
+ extra_rdoc_files:
82
+ - LICENSE
83
+ - README.rdoc
84
+ files:
85
+ - .document
86
+ - .gitignore
87
+ - LICENSE
88
+ - README.rdoc
89
+ - Rakefile
90
+ - VERSION
91
+ - examples/filter.rb
92
+ - examples/helper.rb
93
+ - examples/slugizer.rb
94
+ - examples/storage.rb
95
+ - examples/types.rb
96
+ - examples/update.rb
97
+ - lib/mongomapper_ext.rb
98
+ - lib/mongomapper_ext/file.rb
99
+ - lib/mongomapper_ext/filter.rb
100
+ - lib/mongomapper_ext/slugizer.rb
101
+ - lib/mongomapper_ext/storage.rb
102
+ - lib/mongomapper_ext/types/open_struct.rb
103
+ - lib/mongomapper_ext/types/set.rb
104
+ - lib/mongomapper_ext/types/timestamp.rb
105
+ - lib/mongomapper_ext/update.rb
106
+ - test/helper.rb
107
+ - test/models.rb
108
+ - test/support/custom_matchers.rb
109
+ - test/test_filter.rb
110
+ - test/test_slugizer.rb
111
+ - test/test_storage.rb
112
+ - test/test_update.rb
113
+ - test/types/test_open_struct.rb
114
+ - test/types/test_set.rb
115
+ - test/types/test_timestamp.rb
116
+ has_rdoc: true
117
+ homepage: http://github.com/dcu/mongomapper_ext
118
+ licenses: []
119
+
120
+ post_install_message:
121
+ rdoc_options:
122
+ - --charset=UTF-8
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: "0"
130
+ version:
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: "0"
136
+ version:
137
+ requirements: []
138
+
139
+ rubyforge_project:
140
+ rubygems_version: 1.3.5
141
+ signing_key:
142
+ specification_version: 3
143
+ summary: MongoMapper extensions
144
+ test_files:
145
+ - test/test_slugizer.rb
146
+ - test/test_filter.rb
147
+ - test/test_storage.rb
148
+ - test/test_update.rb
149
+ - test/support/custom_matchers.rb
150
+ - test/models.rb
151
+ - test/helper.rb
152
+ - test/types/test_open_struct.rb
153
+ - test/types/test_timestamp.rb
154
+ - test/types/test_set.rb
155
+ - examples/types.rb
156
+ - examples/filter.rb
157
+ - examples/update.rb
158
+ - examples/storage.rb
159
+ - examples/helper.rb
160
+ - examples/slugizer.rb