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