mongoid_ext 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +50 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/bin/mongoid_console +85 -0
- data/lib/mongoid_ext.rb +71 -0
- data/lib/mongoid_ext/criteria_ext.rb +15 -0
- data/lib/mongoid_ext/document_ext.rb +29 -0
- data/lib/mongoid_ext/file.rb +86 -0
- data/lib/mongoid_ext/file_list.rb +74 -0
- data/lib/mongoid_ext/file_server.rb +69 -0
- data/lib/mongoid_ext/filter.rb +266 -0
- data/lib/mongoid_ext/filter/parser.rb +71 -0
- data/lib/mongoid_ext/filter/result_set.rb +75 -0
- data/lib/mongoid_ext/js/filter.js +41 -0
- data/lib/mongoid_ext/js/find_tags.js +26 -0
- data/lib/mongoid_ext/js/tag_cloud.js +28 -0
- data/lib/mongoid_ext/modifiers.rb +93 -0
- data/lib/mongoid_ext/mongo_mapper.rb +63 -0
- data/lib/mongoid_ext/paranoia.rb +100 -0
- data/lib/mongoid_ext/patches.rb +17 -0
- data/lib/mongoid_ext/random.rb +23 -0
- data/lib/mongoid_ext/slugizer.rb +84 -0
- data/lib/mongoid_ext/storage.rb +110 -0
- data/lib/mongoid_ext/tags.rb +26 -0
- data/lib/mongoid_ext/types/embedded_hash.rb +25 -0
- data/lib/mongoid_ext/types/open_struct.rb +15 -0
- data/lib/mongoid_ext/types/timestamp.rb +15 -0
- data/lib/mongoid_ext/types/translation.rb +51 -0
- data/lib/mongoid_ext/update.rb +11 -0
- data/lib/mongoid_ext/versioning.rb +189 -0
- data/lib/mongoid_ext/voteable.rb +104 -0
- data/mongoid_ext.gemspec +129 -0
- data/test/helper.rb +30 -0
- data/test/models.rb +80 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/test_filter.rb +51 -0
- data/test/test_modifiers.rb +65 -0
- data/test/test_paranoia.rb +40 -0
- data/test/test_random.rb +57 -0
- data/test/test_slugizer.rb +66 -0
- data/test/test_storage.rb +110 -0
- data/test/test_tags.rb +47 -0
- data/test/test_update.rb +16 -0
- data/test/test_versioning.rb +55 -0
- data/test/test_voteable.rb +77 -0
- data/test/types/test_open_struct.rb +22 -0
- data/test/types/test_set.rb +26 -0
- data/test/types/test_timestamp.rb +40 -0
- metadata +301 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoidExt
|
3
|
+
module Filter
|
4
|
+
class ResultSet
|
5
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|respond_to\?|proxy_|^object_id$)/ }
|
6
|
+
|
7
|
+
attr_accessor :subject
|
8
|
+
attr_reader :total_entries, :per_page, :current_page
|
9
|
+
attr_reader :parsed_query
|
10
|
+
attr_reader :mongo_query
|
11
|
+
alias limit per_page
|
12
|
+
|
13
|
+
def initialize(total_entries, parsed_query, mongo_query, current_page, per_page=nil)
|
14
|
+
@total_entries = total_entries.to_i
|
15
|
+
@mongo_query = mongo_query
|
16
|
+
@parsed_query = parsed_query
|
17
|
+
self.per_page = per_page
|
18
|
+
self.current_page = current_page
|
19
|
+
end
|
20
|
+
|
21
|
+
def total_pages
|
22
|
+
(total_entries / per_page.to_f).ceil
|
23
|
+
end
|
24
|
+
|
25
|
+
def out_of_bounds?
|
26
|
+
current_page > total_pages
|
27
|
+
end
|
28
|
+
|
29
|
+
def previous_page
|
30
|
+
current_page > 1 ? (current_page - 1) : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_page
|
34
|
+
current_page < total_pages ? (current_page + 1) : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def skip
|
38
|
+
(current_page - 1) * per_page
|
39
|
+
end
|
40
|
+
alias offset skip # for will paginate support
|
41
|
+
|
42
|
+
def send(method, *args, &block)
|
43
|
+
if respond_to?(method)
|
44
|
+
super
|
45
|
+
else
|
46
|
+
subject.send(method, *args, &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def ===(other)
|
51
|
+
other === subject
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_missing(name, *args, &block)
|
55
|
+
@subject.send(name, *args, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def respond_to?(name, *args, &block)
|
59
|
+
super || @subject.respond_to?(name, *args, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def per_page=(value)
|
64
|
+
value = 25 if value.blank?
|
65
|
+
@per_page = value.to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
def current_page=(value)
|
69
|
+
value = value.to_i
|
70
|
+
value = 1 if value < 1
|
71
|
+
@current_page = value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
function filter(collection, q, config) {
|
2
|
+
var results = [];
|
3
|
+
var counter = 0;
|
4
|
+
|
5
|
+
var fields = {_keywords: 1};
|
6
|
+
for(var i in config.select) {
|
7
|
+
fields[config.select[i]] = 1;
|
8
|
+
}
|
9
|
+
|
10
|
+
var time = new Date().getTime();
|
11
|
+
db[collection].find(q, fields).limit(500).forEach(
|
12
|
+
function(doc) {
|
13
|
+
var score = 0.0;
|
14
|
+
for(var i in config.words) {
|
15
|
+
var word = config.words[i];
|
16
|
+
if(doc._keywords.indexOf(word) != -1 )
|
17
|
+
score += 15.0;
|
18
|
+
}
|
19
|
+
|
20
|
+
for(var i in config.stemmed) {
|
21
|
+
var word = config.stemmed[i];
|
22
|
+
if(doc._keywords.indexOf(word) != -1 )
|
23
|
+
score += (1.0 + word.length);
|
24
|
+
}
|
25
|
+
|
26
|
+
if(score >= config.min_score || 1.0 ) {
|
27
|
+
delete doc._keywords;
|
28
|
+
results.push({'score': score, 'doc': doc});
|
29
|
+
counter += 1;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
);
|
33
|
+
|
34
|
+
var sorted = results.sort(function(a,b) {
|
35
|
+
return b.score - a.score;
|
36
|
+
});
|
37
|
+
|
38
|
+
time = (new Date().getTime() - time);
|
39
|
+
|
40
|
+
return {total_entries: counter, elapsed_time: time, results: sorted.slice(0, config.limit||500)};
|
41
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
function find_tags(collection, regex, query, limit) {
|
2
|
+
var counter = function(collection, regex, query){
|
3
|
+
var counts = {};
|
4
|
+
db[collection].find(query, {"tags":1}).limit(500).forEach(
|
5
|
+
function(p){
|
6
|
+
if ( p.tags ){
|
7
|
+
for ( var i=0; i<p.tags.length; i++ ){
|
8
|
+
var name = p.tags[i];
|
9
|
+
if(name.match(regex) != null)
|
10
|
+
counts[name] = 1 + ( counts[name] || 0 );
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
);
|
15
|
+
return counts;
|
16
|
+
};
|
17
|
+
|
18
|
+
var counts = counter(collection, regex, query);
|
19
|
+
|
20
|
+
var tags = [];
|
21
|
+
for ( var tag in counts ){
|
22
|
+
tags.push( { name : tag , count : counts[tag] } )
|
23
|
+
}
|
24
|
+
|
25
|
+
return tags;
|
26
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// TODO: port it to map reduce
|
2
|
+
function tagCloud(collection, q, limit) {
|
3
|
+
var counter = function(collection, q){
|
4
|
+
var counts = {constructor: 0};
|
5
|
+
db[collection].find(q, {"tags":1}).limit(500).forEach(
|
6
|
+
function(p){
|
7
|
+
if ( p.tags ){
|
8
|
+
for ( var i=0; i<p.tags.length; i++ ){
|
9
|
+
var name = p.tags[i];
|
10
|
+
counts[name] = 1 + ( counts[name] || 0 );
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
);
|
15
|
+
if(counts["constructor"] == 0) { delete counts.constructor; }
|
16
|
+
return counts;
|
17
|
+
};
|
18
|
+
|
19
|
+
var counts = counter(collection, q);
|
20
|
+
|
21
|
+
// maybe sort to by nice
|
22
|
+
var sorted = [];
|
23
|
+
for ( var tag in counts ){
|
24
|
+
sorted.push( { name : tag , count : counts[tag] } )
|
25
|
+
}
|
26
|
+
|
27
|
+
return sorted.slice(0,limit||30);
|
28
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoidExt
|
3
|
+
module Modifiers
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def increment(conditions, update)
|
8
|
+
_apply_modifier('$inc', conditions, update)
|
9
|
+
end
|
10
|
+
|
11
|
+
def decrement(conditions, update)
|
12
|
+
update.each do |k, v|
|
13
|
+
update[k] = -v.abs
|
14
|
+
end
|
15
|
+
|
16
|
+
_apply_modifier('$inc', conditions, update)
|
17
|
+
end
|
18
|
+
|
19
|
+
def override(conditions, update) # set() is already taken :(
|
20
|
+
_apply_modifier('$set', conditions, update)
|
21
|
+
end
|
22
|
+
|
23
|
+
def unset(conditions, update)
|
24
|
+
_apply_modifier('$unset', conditions, update)
|
25
|
+
end
|
26
|
+
|
27
|
+
def push(conditions, update)
|
28
|
+
_apply_modifier('$push', conditions, update)
|
29
|
+
end
|
30
|
+
|
31
|
+
def push_all(conditions, update)
|
32
|
+
_apply_modifier('$pushAll', conditions, update)
|
33
|
+
end
|
34
|
+
|
35
|
+
def push_uniq(conditions, update)
|
36
|
+
_apply_modifier('$addToSet', conditions, update)
|
37
|
+
end
|
38
|
+
|
39
|
+
def pull(conditions, update)
|
40
|
+
_apply_modifier('$pull', conditions, update)
|
41
|
+
end
|
42
|
+
|
43
|
+
def pull_all(conditions, update)
|
44
|
+
_apply_modifier('$pullAll', conditions, update)
|
45
|
+
end
|
46
|
+
|
47
|
+
def pop(conditions, update)
|
48
|
+
_apply_modifier('$pop', conditions, update)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def _apply_modifier(modifier, conditions, update)
|
53
|
+
collection.update(conditions, {modifier => update}, :multi => true)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module InstanceMethods
|
58
|
+
def unset(update)
|
59
|
+
self.class.unset({:_id => id}, update)
|
60
|
+
end
|
61
|
+
|
62
|
+
def increment(update)
|
63
|
+
self.class.increment({:_id => id}, update)
|
64
|
+
end
|
65
|
+
|
66
|
+
def decrement(update)
|
67
|
+
self.class.decrement({:_id => id}, update)
|
68
|
+
end
|
69
|
+
|
70
|
+
def override(update)
|
71
|
+
self.class.override({:_id => id}, update)
|
72
|
+
end
|
73
|
+
|
74
|
+
def push(update)
|
75
|
+
self.class.push({:_id => id}, update)
|
76
|
+
end
|
77
|
+
|
78
|
+
def pull(update)
|
79
|
+
self.class.pull({:_id => id}, update)
|
80
|
+
end
|
81
|
+
|
82
|
+
def push_uniq(update)
|
83
|
+
self.class.push_uniq({:_id => id}, update)
|
84
|
+
end
|
85
|
+
|
86
|
+
def pop(update)
|
87
|
+
self.class.pop({:_id => id}, update)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
Mongoid::Document.send(:include, MongoidExt::Modifiers)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module MongoidExt
|
2
|
+
module MongoMapper
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def find_each(*args, &block)
|
10
|
+
all(*args).each do |doc|
|
11
|
+
block.call(doc)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def many(name, opts = {})
|
16
|
+
opts[:stored_as] = :array if opts.delete(:in)
|
17
|
+
if fkey = opts.delete(:foreign_key)
|
18
|
+
opts[:inverse_of] = fkey.sub(/_id$/, "")
|
19
|
+
end
|
20
|
+
|
21
|
+
references_many name, opts
|
22
|
+
end
|
23
|
+
alias :has_many :many
|
24
|
+
|
25
|
+
def one(name, opts = {})
|
26
|
+
if fkey = opts.delete(:foreign_key)
|
27
|
+
opts[:inverse_of] = fkey.sub(/_id$/, "")
|
28
|
+
end
|
29
|
+
|
30
|
+
references_one name, opts
|
31
|
+
end
|
32
|
+
alias :has_one :one
|
33
|
+
|
34
|
+
def belongs_to(name, opts = {})
|
35
|
+
if fkey = opts.delete(:foreign_key)
|
36
|
+
opts[:inverse_of] = fkey.sub(/_id$/, "")
|
37
|
+
end
|
38
|
+
|
39
|
+
if opts[:polymorphic]
|
40
|
+
raise ArgumentError, "polymorphic associations are not supported yet"
|
41
|
+
end
|
42
|
+
|
43
|
+
referenced_in name, opts
|
44
|
+
end
|
45
|
+
|
46
|
+
def timestamps!
|
47
|
+
include Mongoid::Timestamps
|
48
|
+
end
|
49
|
+
|
50
|
+
def key(name, *args)
|
51
|
+
opts = args.extract_options!
|
52
|
+
|
53
|
+
opts[:type] = args.first if !args.empty?
|
54
|
+
|
55
|
+
field name, opts
|
56
|
+
end
|
57
|
+
|
58
|
+
def ensure_index(*args)
|
59
|
+
index *args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module MongoidExt #:nodoc:
|
3
|
+
# Include this module to get soft deletion of root level documents.
|
4
|
+
# This will add a deleted_at field to the +Document+, managed automatically.
|
5
|
+
# Potentially incompatible with unique indices. (if collisions with deleted items)
|
6
|
+
#
|
7
|
+
# To use:
|
8
|
+
#
|
9
|
+
# class Person
|
10
|
+
# include Mongoid::Document
|
11
|
+
# include MongoidExt::Paranoia
|
12
|
+
# end
|
13
|
+
module Paranoia
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
included do
|
17
|
+
end
|
18
|
+
|
19
|
+
# Delete the paranoid +Document+ from the database completely. This will
|
20
|
+
# run the destroy callbacks.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# <tt>document.destroy!</tt>
|
25
|
+
def destroy!
|
26
|
+
run_callbacks(:destroy) { delete! }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Delete the paranoid +Document+ from the database completely.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
#
|
33
|
+
# <tt>document.delete!</tt>
|
34
|
+
def delete!
|
35
|
+
Mongoid::Persistence::Remove.new(self).persist
|
36
|
+
end
|
37
|
+
|
38
|
+
# Delete the +Document+, will set the deleted_at timestamp and not actually
|
39
|
+
# delete it.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# <tt>document.remove</tt>
|
44
|
+
#
|
45
|
+
# Returns:
|
46
|
+
#
|
47
|
+
# true
|
48
|
+
def remove(options = {})
|
49
|
+
self.class.paranoia_klass.create(:document => self.raw_attributes)
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
alias :delete :remove
|
54
|
+
|
55
|
+
module ClassMethods #:nodoc:
|
56
|
+
# Find deleted documents
|
57
|
+
#
|
58
|
+
# Examples:
|
59
|
+
#
|
60
|
+
# <tt>Person.deleted</tt> # all deleted employees
|
61
|
+
# <tt>Company.first.employees.deleted</tt> # works with a join
|
62
|
+
# <tt>Person.deleted.find("4c188dea7b17235a2a000001").first</tt>
|
63
|
+
# <tt>Person.deleted.compact!(1.week.ago)</tt>
|
64
|
+
def deleted
|
65
|
+
self.paranoia_klass
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def paranoia_klass
|
70
|
+
parent_klass = self
|
71
|
+
@paranoia_klass ||= Class.new do
|
72
|
+
include Mongoid::Document
|
73
|
+
include Mongoid::Timestamps
|
74
|
+
|
75
|
+
cattr_accessor :parent_class
|
76
|
+
self.parent_class = parent_klass
|
77
|
+
|
78
|
+
self.collection_name = "#{self.parent_class.collection_name}.trash"
|
79
|
+
|
80
|
+
field :document, :type => Hash
|
81
|
+
|
82
|
+
before_validation :set_id, :on => :create
|
83
|
+
|
84
|
+
def restore
|
85
|
+
self.class.parent_class.create(self.document)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.compact!(date = 1.month.ago)
|
89
|
+
self.delete_all(:created_at.lte => date)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def set_id
|
94
|
+
self["_id"] = self.document["_id"]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Mongo
|
2
|
+
class DB
|
3
|
+
def nolock_eval(code, *args)
|
4
|
+
if not code.is_a? BSON::Code
|
5
|
+
code = BSON::Code.new(code)
|
6
|
+
end
|
7
|
+
|
8
|
+
oh = BSON::OrderedHash.new
|
9
|
+
oh[:$eval] = code
|
10
|
+
oh[:args] = args
|
11
|
+
oh[:nolock] = true
|
12
|
+
|
13
|
+
doc = command(oh)
|
14
|
+
doc['retval']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|