mongoid_ext 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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