mongomapper_ext 0.0.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/.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