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.
- 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
|