mongoid_ext 0.6.1

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.
Files changed (53) hide show
  1. data/.document +5 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +50 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +17 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/bin/mongoid_console +85 -0
  9. data/lib/mongoid_ext.rb +71 -0
  10. data/lib/mongoid_ext/criteria_ext.rb +15 -0
  11. data/lib/mongoid_ext/document_ext.rb +29 -0
  12. data/lib/mongoid_ext/file.rb +86 -0
  13. data/lib/mongoid_ext/file_list.rb +74 -0
  14. data/lib/mongoid_ext/file_server.rb +69 -0
  15. data/lib/mongoid_ext/filter.rb +266 -0
  16. data/lib/mongoid_ext/filter/parser.rb +71 -0
  17. data/lib/mongoid_ext/filter/result_set.rb +75 -0
  18. data/lib/mongoid_ext/js/filter.js +41 -0
  19. data/lib/mongoid_ext/js/find_tags.js +26 -0
  20. data/lib/mongoid_ext/js/tag_cloud.js +28 -0
  21. data/lib/mongoid_ext/modifiers.rb +93 -0
  22. data/lib/mongoid_ext/mongo_mapper.rb +63 -0
  23. data/lib/mongoid_ext/paranoia.rb +100 -0
  24. data/lib/mongoid_ext/patches.rb +17 -0
  25. data/lib/mongoid_ext/random.rb +23 -0
  26. data/lib/mongoid_ext/slugizer.rb +84 -0
  27. data/lib/mongoid_ext/storage.rb +110 -0
  28. data/lib/mongoid_ext/tags.rb +26 -0
  29. data/lib/mongoid_ext/types/embedded_hash.rb +25 -0
  30. data/lib/mongoid_ext/types/open_struct.rb +15 -0
  31. data/lib/mongoid_ext/types/timestamp.rb +15 -0
  32. data/lib/mongoid_ext/types/translation.rb +51 -0
  33. data/lib/mongoid_ext/update.rb +11 -0
  34. data/lib/mongoid_ext/versioning.rb +189 -0
  35. data/lib/mongoid_ext/voteable.rb +104 -0
  36. data/mongoid_ext.gemspec +129 -0
  37. data/test/helper.rb +30 -0
  38. data/test/models.rb +80 -0
  39. data/test/support/custom_matchers.rb +55 -0
  40. data/test/test_filter.rb +51 -0
  41. data/test/test_modifiers.rb +65 -0
  42. data/test/test_paranoia.rb +40 -0
  43. data/test/test_random.rb +57 -0
  44. data/test/test_slugizer.rb +66 -0
  45. data/test/test_storage.rb +110 -0
  46. data/test/test_tags.rb +47 -0
  47. data/test/test_update.rb +16 -0
  48. data/test/test_versioning.rb +55 -0
  49. data/test/test_voteable.rb +77 -0
  50. data/test/types/test_open_struct.rb +22 -0
  51. data/test/types/test_set.rb +26 -0
  52. data/test/types/test_timestamp.rb +40 -0
  53. metadata +301 -0
@@ -0,0 +1,104 @@
1
+ module MongoidExt
2
+ module Voteable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ field :votes_count, :type => Integer, :default => 0
7
+ field :votes_average, :type => Integer, :default => 0
8
+ field :votes_up, :type => Integer, :default => 0
9
+ field :votes_down, :type => Integer, :default => 0
10
+
11
+ field :votes, :type => Hash, :default => {}
12
+ end
13
+
14
+ def voted?(voter_id)
15
+ if self[:votes] && !self[:votes].empty?
16
+ self[:votes].include?(voter_id)
17
+ else
18
+ self.class.exists?(:conditions => {:_id => self.id, :"votes.#{voter_id}".exists => true})
19
+ end
20
+ end
21
+
22
+ def vote!(value, voter_id, &block)
23
+ old_vote = self.votes[voter_id]
24
+ if !old_vote
25
+ self.votes[voter_id] = value
26
+ self.save(:validate => false)
27
+
28
+ add_vote!(value, voter_id, &block)
29
+ return :created
30
+ else
31
+ if(old_vote != value)
32
+ self.votes[voter_id] = value
33
+ self.save
34
+ self.remove_vote!(old_vote, voter_id, &block)
35
+ self.add_vote!(value, voter_id, &block)
36
+
37
+ return :updated
38
+ else
39
+ self.votes.delete(voter_id)
40
+ self.save(:validate => false)
41
+ remove_vote!(value, voter_id, &block)
42
+ return :destroyed
43
+ end
44
+ end
45
+ end
46
+
47
+ def add_vote!(value, voter_id, &block)
48
+ if embedded?
49
+ updates = {self._position+".votes_count" => 1,
50
+ self._position+".votes_average" => value.to_i}
51
+ if value == 1
52
+ updates[self._position+".votes_up"] = 1
53
+ elsif value == -1
54
+ updates[self._position+".votes_down"] = 1
55
+ end
56
+
57
+ self._parent.increment(updates)
58
+ else
59
+ updates = {:votes_count => 1, :votes_average => value.to_i}
60
+ if value == 1
61
+ updates[:votes_up] = 1
62
+ elsif value == -1
63
+ updates[:votes_down] = 1
64
+ end
65
+
66
+ self.increment(updates)
67
+ end
68
+
69
+ block.call(value, :add) if block
70
+
71
+ self.on_add_vote(value, voter_id) if self.respond_to?(:on_add_vote)
72
+ end
73
+
74
+ def remove_vote!(value, voter_id, &block)
75
+ if embedded?
76
+ updates = {self._position+".votes_count" => -1,
77
+ self._position+".votes_average" => -value.to_i}
78
+ if value == 1
79
+ updates[self._position+".votes_up"] = -1
80
+ elsif value == -1
81
+ updates[self._position+".votes_down"] = -1
82
+ end
83
+
84
+ self._parent.increment(updates)
85
+ else
86
+ updates = {:votes_count => -1, :votes_average => -value}
87
+ if value == 1
88
+ updates[:votes_up] = -1
89
+ elsif value == -1
90
+ updates[:votes_down] = -1
91
+ end
92
+
93
+ self.increment(updates)
94
+ end
95
+
96
+ block.call(value, :remove) if block
97
+
98
+ self.on_remove_vote(value, voter_id) if self.respond_to?(:on_remove_vote)
99
+ end
100
+
101
+ module ClassMethods
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,129 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mongoid_ext}
8
+ s.version = "0.6.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["David A. Cuadrado"]
12
+ s.date = %q{2011-06-25}
13
+ s.default_executable = %q{mongoid_console}
14
+ s.description = %q{mongoid plugins}
15
+ s.email = %q{krawek@gmail.com}
16
+ s.executables = ["mongoid_console"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/mongoid_console",
30
+ "lib/mongoid_ext.rb",
31
+ "lib/mongoid_ext/criteria_ext.rb",
32
+ "lib/mongoid_ext/document_ext.rb",
33
+ "lib/mongoid_ext/file.rb",
34
+ "lib/mongoid_ext/file_list.rb",
35
+ "lib/mongoid_ext/file_server.rb",
36
+ "lib/mongoid_ext/filter.rb",
37
+ "lib/mongoid_ext/filter/parser.rb",
38
+ "lib/mongoid_ext/filter/result_set.rb",
39
+ "lib/mongoid_ext/js/filter.js",
40
+ "lib/mongoid_ext/js/find_tags.js",
41
+ "lib/mongoid_ext/js/tag_cloud.js",
42
+ "lib/mongoid_ext/modifiers.rb",
43
+ "lib/mongoid_ext/mongo_mapper.rb",
44
+ "lib/mongoid_ext/paranoia.rb",
45
+ "lib/mongoid_ext/patches.rb",
46
+ "lib/mongoid_ext/random.rb",
47
+ "lib/mongoid_ext/slugizer.rb",
48
+ "lib/mongoid_ext/storage.rb",
49
+ "lib/mongoid_ext/tags.rb",
50
+ "lib/mongoid_ext/types/embedded_hash.rb",
51
+ "lib/mongoid_ext/types/open_struct.rb",
52
+ "lib/mongoid_ext/types/timestamp.rb",
53
+ "lib/mongoid_ext/types/translation.rb",
54
+ "lib/mongoid_ext/update.rb",
55
+ "lib/mongoid_ext/versioning.rb",
56
+ "lib/mongoid_ext/voteable.rb",
57
+ "mongoid_ext.gemspec",
58
+ "test/helper.rb",
59
+ "test/models.rb",
60
+ "test/support/custom_matchers.rb",
61
+ "test/test_filter.rb",
62
+ "test/test_modifiers.rb",
63
+ "test/test_paranoia.rb",
64
+ "test/test_random.rb",
65
+ "test/test_slugizer.rb",
66
+ "test/test_storage.rb",
67
+ "test/test_tags.rb",
68
+ "test/test_update.rb",
69
+ "test/test_versioning.rb",
70
+ "test/test_voteable.rb",
71
+ "test/types/test_open_struct.rb",
72
+ "test/types/test_set.rb",
73
+ "test/types/test_timestamp.rb"
74
+ ]
75
+ s.homepage = %q{http://github.com/dcu/mongoid_ext}
76
+ s.require_paths = ["lib"]
77
+ s.rubygems_version = %q{1.3.7}
78
+ s.summary = %q{mongoid plugins}
79
+
80
+ if s.respond_to? :specification_version then
81
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
82
+ s.specification_version = 3
83
+
84
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
85
+ s.add_runtime_dependency(%q<mongoid>, ["~> 2"])
86
+ s.add_runtime_dependency(%q<uuidtools>, [">= 2.1.1"])
87
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
88
+ s.add_runtime_dependency(%q<tzinfo>, [">= 0"])
89
+ s.add_runtime_dependency(%q<differ>, [">= 0.1.2"])
90
+ s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
91
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
92
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
93
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
94
+ s.add_development_dependency(%q<jnunemaker-matchy>, ["~> 0.4"])
95
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
96
+ s.add_development_dependency(%q<mocha>, ["~> 0.9.4"])
97
+ s.add_development_dependency(%q<timecop>, [">= 0"])
98
+ else
99
+ s.add_dependency(%q<mongoid>, ["~> 2"])
100
+ s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
101
+ s.add_dependency(%q<i18n>, [">= 0"])
102
+ s.add_dependency(%q<tzinfo>, [">= 0"])
103
+ s.add_dependency(%q<differ>, [">= 0.1.2"])
104
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
105
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
106
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
107
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
108
+ s.add_dependency(%q<jnunemaker-matchy>, ["~> 0.4"])
109
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
110
+ s.add_dependency(%q<mocha>, ["~> 0.9.4"])
111
+ s.add_dependency(%q<timecop>, [">= 0"])
112
+ end
113
+ else
114
+ s.add_dependency(%q<mongoid>, ["~> 2"])
115
+ s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
116
+ s.add_dependency(%q<i18n>, [">= 0"])
117
+ s.add_dependency(%q<tzinfo>, [">= 0"])
118
+ s.add_dependency(%q<differ>, [">= 0.1.2"])
119
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
120
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
121
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
122
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
123
+ s.add_dependency(%q<jnunemaker-matchy>, ["~> 0.4"])
124
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
125
+ s.add_dependency(%q<mocha>, ["~> 0.9.4"])
126
+ s.add_dependency(%q<timecop>, [">= 0"])
127
+ end
128
+ end
129
+
data/test/helper.rb ADDED
@@ -0,0 +1,30 @@
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 'mongoid_ext'
17
+
18
+ Mongoid.configure do |config|
19
+ config.master = Mongo::Connection.new.db("test")
20
+ end
21
+
22
+ require 'models'
23
+
24
+ class Test::Unit::TestCase
25
+ include CustomMatchers
26
+ end
27
+
28
+ MongoidExt.init
29
+
30
+ $VERBOSE=nil
data/test/models.rb ADDED
@@ -0,0 +1,80 @@
1
+
2
+ class Event # for safe_update, and Timestamp
3
+ include Mongoid::Document
4
+
5
+ field :start_date, :type => Timestamp
6
+ field :end_date, :type => Timestamp
7
+
8
+ field :password, :type => String
9
+ end
10
+
11
+ class Recipe # for Set
12
+ include Mongoid::Document
13
+ include MongoidExt::Filter
14
+
15
+ language Proc.new { |d| d.language }
16
+ filterable_keys :language
17
+
18
+ field :ingredients, :type => Set
19
+ field :description, :type => String
20
+ field :language, :type => String, :default => 'en'
21
+ end
22
+
23
+ class Avatar # for Storage and File
24
+ include Mongoid::Document
25
+ include MongoidExt::Storage
26
+
27
+ file_key :data
28
+
29
+ file_list :alternatives
30
+ file_key :first_alternative, :in => :alternatives
31
+ end
32
+
33
+ class UserConfig #for OpenStruct
34
+ include Mongoid::Document
35
+ field :entries, :type => OpenStruct
36
+ end
37
+
38
+ class User
39
+ include Mongoid::Document
40
+ include MongoidExt::Paranoia
41
+ include MongoidExt::Voteable
42
+
43
+ field :login
44
+ field :email
45
+ end
46
+
47
+ class BlogPost # for Slug and Filter
48
+ include Mongoid::Document
49
+ include MongoidExt::Filter
50
+ include MongoidExt::Slugizer
51
+ include MongoidExt::Tags
52
+ include MongoidExt::Versioning
53
+
54
+ filterable_keys :title, :body, :tags, :date
55
+ slug_key :title, :max_length => 18, :min_length => 3, :callback_type => :before_validation, :add_prefix => true
56
+ language :find_language
57
+
58
+ field :title, :type => String
59
+ field :body, :type => String
60
+ field :tags, :type => Array
61
+ field :date, :type => Time
62
+
63
+ referenced_in :updated_by, :class_name => "User"
64
+
65
+ versionable_keys :title, :body, :tags, :owner_field => "updated_by_id", :max_versions => 2
66
+
67
+ def find_language
68
+ 'en'
69
+ end
70
+ end
71
+
72
+ class Entry
73
+ include Mongoid::Document
74
+ include MongoidExt::Random
75
+
76
+ field :v, :type => Integer
77
+ field :a, :type => Array
78
+ end
79
+ Entry.delete_all
80
+ 100.times {|v| Entry.create(:v => v)}
@@ -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,51 @@
1
+ # encoding: utf-8
2
+ require 'helper'
3
+
4
+ class TestFilter < Test::Unit::TestCase
5
+ context "filtering data" do
6
+ setup do
7
+ BlogPost.delete_all
8
+ @blogpost = BlogPost.create!(:title => "%How dOEs tHIs Work?!",
9
+ :body => "HeRe is tHe Body of the bLog pOsT",
10
+ :tags => ["my", "list", "of", "tags"])
11
+ @entradablog = BlogPost.create!(:title => "sobre las piña",
12
+ :body => "la piña no es un árbol",
13
+ :tags => ["frutas"])
14
+ end
15
+
16
+ should "be case insensitive" do
17
+ BlogPost.filter("body").should == [@blogpost]
18
+ end
19
+
20
+ should "be able to find by title" do
21
+ BlogPost.filter("this").should == [@blogpost]
22
+ end
23
+
24
+ should "be able to find by body" do
25
+ BlogPost.filter("blog").should == [@blogpost]
26
+ end
27
+
28
+ should "be able to find by tags" do
29
+ BlogPost.filter("list").should == [@blogpost]
30
+ end
31
+
32
+ should "be able to find by title or body" do
33
+ BlogPost.filter("work blog").should == [@blogpost]
34
+ end
35
+
36
+ should "ignore inexistant words" do
37
+ BlogPost.filter("work lalala").should == [@blogpost]
38
+ end
39
+
40
+ should "normalize the text" do
41
+ BlogPost.filter("pina").should == [@entradablog]
42
+ BlogPost.filter("arbol").should == [@entradablog]
43
+ end
44
+
45
+ should "allow to paginate results" do
46
+ results = BlogPost.filter("tag", :per_page => 1, :page => 1)
47
+ results.should == [@blogpost]
48
+ results.total_pages.should == 1
49
+ end
50
+ end
51
+ end