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