mongomapper_ext 0.1.5 → 0.2.0
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/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/i18n.rb +32 -0
- data/examples/storage.rb +24 -7
- data/lib/mongomapper_ext/file.rb +66 -27
- data/lib/mongomapper_ext/file_list.rb +71 -0
- data/lib/mongomapper_ext/js/find_tags.js +12 -7
- data/lib/mongomapper_ext/slugizer.rb +14 -3
- data/lib/mongomapper_ext/storage.rb +111 -50
- data/lib/mongomapper_ext/types/translation.rb +51 -0
- data/lib/mongomapper_ext.rb +10 -1
- data/mongomapper_ext.gemspec +9 -5
- data/test/models.rb +4 -1
- data/test/test_slugizer.rb +15 -0
- data/test/test_storage.rb +32 -5
- data/test/test_tags.rb +3 -2
- metadata +11 -7
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/dcu/mongomapper_ext"
|
12
12
|
gem.authors = ["David A. Cuadrado"]
|
13
13
|
|
14
|
-
gem.add_dependency('mongo_mapper', '>= 0.
|
14
|
+
gem.add_dependency('mongo_mapper', '>= 0.7.3')
|
15
15
|
gem.add_dependency('uuidtools', '>= 2.0.0')
|
16
16
|
|
17
17
|
gem.add_development_dependency("shoulda", ">= 2.10.2")
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/examples/i18n.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require './helper'
|
2
|
+
require 'mongomapper_ext/types/translation'
|
3
|
+
|
4
|
+
class TranslationEx
|
5
|
+
include MongoMapper::Document
|
6
|
+
|
7
|
+
key :title, Translation, :default => Translation.build({"es" => "titulo", "en" => "title"}, "en")
|
8
|
+
key :body, Translation, :default => Translation.build({"es" => "contenido", "en" => "content"}, "en")
|
9
|
+
end
|
10
|
+
|
11
|
+
TranslationEx.delete_all
|
12
|
+
o1 = TranslationEx.create
|
13
|
+
o2 = TranslationEx.create
|
14
|
+
|
15
|
+
|
16
|
+
o1.title = "my new title"
|
17
|
+
puts o1.title[:es]
|
18
|
+
|
19
|
+
o1.title[:es] = "mi nuevo titulo"
|
20
|
+
o1.save
|
21
|
+
|
22
|
+
puts "languages: #{o1.title.languages.inspect}"
|
23
|
+
puts "default text: #{o1.title}"
|
24
|
+
|
25
|
+
o1 = TranslationEx.find(o1.id)
|
26
|
+
o2 = TranslationEx.find(o2.id)
|
27
|
+
|
28
|
+
puts o1.to_mongo.inspect
|
29
|
+
|
30
|
+
|
31
|
+
TranslationEx.delete_all
|
32
|
+
|
data/examples/storage.rb
CHANGED
@@ -4,19 +4,36 @@ class StorageEx
|
|
4
4
|
include MongoMapper::Document
|
5
5
|
include MongoMapperExt::Storage
|
6
6
|
|
7
|
-
file_key :
|
7
|
+
file_key :default_file
|
8
|
+
|
9
|
+
file_list :attachments
|
10
|
+
file_key :an_attachment, :in => :attachments
|
8
11
|
end
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
default_file = StringIO.new("default file content")
|
14
|
+
custom_attachment = StringIO.new("custom attachment content")
|
15
|
+
|
16
|
+
attachment = File.open(__FILE__)
|
17
|
+
|
18
|
+
StorageEx.destroy_all
|
12
19
|
|
13
20
|
s = StorageEx.new
|
14
|
-
|
15
|
-
s.
|
21
|
+
|
22
|
+
s.default_file = default_file
|
23
|
+
s.attachments.put("custom_attachment.txt", custom_attachment)
|
24
|
+
s.an_attachment = attachment
|
16
25
|
|
17
26
|
s.save
|
18
27
|
|
28
|
+
|
19
29
|
from_db = StorageEx.find(s.id)
|
20
30
|
|
21
|
-
puts
|
22
|
-
puts from_db.
|
31
|
+
puts "READ DEFAULT FILE"
|
32
|
+
puts from_db.default_file.read
|
33
|
+
|
34
|
+
puts "READ CUSTOM ATTACHMENT"
|
35
|
+
puts from_db.attachments.get("custom_attachment.txt").read
|
36
|
+
|
37
|
+
puts "READ NAMED ATTACHMENT"
|
38
|
+
puts from_db.an_attachment.read.inspect
|
39
|
+
puts "MIME TYPE: #{from_db.an_attachment.mime_type}"
|
data/lib/mongomapper_ext/file.rb
CHANGED
@@ -1,42 +1,81 @@
|
|
1
1
|
module MongoMapperExt
|
2
|
-
class File
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
2
|
+
class File
|
3
|
+
include MongoMapper::EmbeddedDocument
|
4
|
+
|
5
|
+
key :_id, String
|
6
|
+
key :name, String
|
7
|
+
key :extension, String
|
8
|
+
key :content_type, String
|
9
|
+
|
10
|
+
alias :filename :name
|
11
|
+
|
12
|
+
def put(filename, io, options = {})
|
13
|
+
options[:_id] = grid_filename
|
14
|
+
|
15
|
+
options[:metadata] ||= {}
|
16
|
+
options[:metadata][:collection] = _root_document.collection.name
|
17
|
+
|
18
|
+
self.name = filename
|
19
|
+
if filename =~ /\.([\w]{2,4})$/
|
20
|
+
self.extension = $1
|
21
|
+
end
|
22
|
+
|
23
|
+
if io.kind_of?(String)
|
24
|
+
io = StringIO.new(io)
|
25
|
+
end
|
26
|
+
|
27
|
+
if defined?(Magic)
|
28
|
+
data = io.read(256) # be nice with memory usage
|
29
|
+
self.content_type = options[:content_type] = Magic.guess_string_mime_type(data.to_s)
|
30
|
+
self.extension ||= options[:content_type].to_s.split("/").last.split("-").last
|
31
|
+
|
32
|
+
if io.respond_to?(:rewind)
|
33
|
+
io.rewind
|
34
|
+
else
|
35
|
+
io.seek(0)
|
14
36
|
end
|
15
37
|
end
|
16
38
|
|
17
|
-
|
39
|
+
gridfs.delete(grid_filename)
|
40
|
+
gridfs.put(io, grid_filename, options).inspect
|
18
41
|
end
|
19
42
|
|
20
|
-
def
|
21
|
-
|
43
|
+
def get
|
44
|
+
gridfs.get(grid_filename)
|
22
45
|
end
|
23
46
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
47
|
+
def grid_filename
|
48
|
+
@grid_filename ||= "#{_root_document.collection.name}/#{self.id}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def mime_type
|
52
|
+
self.content_type || get.content_type
|
53
|
+
end
|
32
54
|
|
33
|
-
|
55
|
+
def size
|
56
|
+
get.file_length
|
57
|
+
end
|
34
58
|
|
35
|
-
|
59
|
+
def read(size = nil)
|
60
|
+
self.get.read(size)
|
61
|
+
end
|
36
62
|
|
37
|
-
|
38
|
-
|
63
|
+
def delete
|
64
|
+
gridfs.delete(grid_filename)
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(name, *args, &block)
|
68
|
+
f = self.get rescue nil
|
69
|
+
if f && f.respond_to?(name)
|
70
|
+
f.send(name, *args, &block)
|
71
|
+
else
|
72
|
+
super(name, *args, &block)
|
39
73
|
end
|
40
74
|
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def gridfs
|
78
|
+
_root_document.class.gridfs
|
79
|
+
end
|
41
80
|
end
|
42
81
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MongoMapperExt
|
2
|
+
class FileList < Hash
|
3
|
+
attr_accessor :parent_document
|
4
|
+
|
5
|
+
def self.to_mongo(value)
|
6
|
+
result = {}
|
7
|
+
value.each do |k, v|
|
8
|
+
result[k] = v.to_mongo
|
9
|
+
end
|
10
|
+
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.from_mongo(value)
|
15
|
+
return value if value.kind_of?(self)
|
16
|
+
|
17
|
+
result = FileList.new
|
18
|
+
(value||{}).each do |k, v|
|
19
|
+
result[k] = v.kind_of?(MongoMapperExt::File) ? v : MongoMapperExt::File.new(v)
|
20
|
+
end
|
21
|
+
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
def put(id, io, metadata = {})
|
26
|
+
if !parent_document.new?
|
27
|
+
filename = id
|
28
|
+
if io.respond_to?(:original_filename)
|
29
|
+
filename = io.original_filename
|
30
|
+
elsif io.respond_to?(:path) && io.path
|
31
|
+
filename = ::File.basename(io.path)
|
32
|
+
elsif io.kind_of?(String)
|
33
|
+
io = StringIO.new(io)
|
34
|
+
end
|
35
|
+
|
36
|
+
get(id).put(filename, io, metadata)
|
37
|
+
else
|
38
|
+
(@_pending_files ||= {})[id] = [io, metadata]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def files
|
43
|
+
self.values
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(id)
|
47
|
+
file = self[id]
|
48
|
+
if file.nil?
|
49
|
+
file = self[id] = MongoMapperExt::File.new
|
50
|
+
end
|
51
|
+
file._parent_document = parent_document
|
52
|
+
file
|
53
|
+
end
|
54
|
+
|
55
|
+
def sync_files
|
56
|
+
if @_pending_files
|
57
|
+
@_pending_files.each do |filename, data|
|
58
|
+
put(filename, data[0], data[1])
|
59
|
+
end
|
60
|
+
@_pending_files = nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def destroy_files
|
65
|
+
self.delete_if do |id, file|
|
66
|
+
file._parent_document = parent_document
|
67
|
+
file.delete
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -1,24 +1,29 @@
|
|
1
|
-
function
|
2
|
-
var
|
1
|
+
function find_tags(collection, regex, query, limit) {
|
2
|
+
var counts = db.eval(
|
3
3
|
function(collection, regex, query){
|
4
|
-
var
|
4
|
+
var counts = {};
|
5
5
|
db[collection].find(query, {"tags":1}).limit(500).forEach(
|
6
6
|
function(p){
|
7
7
|
if ( p.tags ){
|
8
8
|
for ( var i=0; i<p.tags.length; i++ ){
|
9
9
|
var name = p.tags[i];
|
10
|
-
if(name.match(regex) != null
|
11
|
-
|
10
|
+
if(name.match(regex) != null)
|
11
|
+
counts[name] = 1 + ( counts[name] || 0 );
|
12
12
|
}
|
13
13
|
}
|
14
14
|
}
|
15
15
|
);
|
16
|
-
return
|
16
|
+
return counts;
|
17
17
|
},
|
18
18
|
collection,
|
19
19
|
regex,
|
20
20
|
query
|
21
21
|
);
|
22
22
|
|
23
|
-
|
23
|
+
var tags = [];
|
24
|
+
for ( var tag in counts ){
|
25
|
+
tags.push( { name : tag , count : counts[tag] } )
|
26
|
+
}
|
27
|
+
|
28
|
+
return tags;
|
24
29
|
}
|
@@ -6,8 +6,6 @@ module MongoMapperExt
|
|
6
6
|
extend Finder
|
7
7
|
|
8
8
|
key :slug, String, :index => true
|
9
|
-
|
10
|
-
before_validation_on_create :generate_slug
|
11
9
|
end
|
12
10
|
end
|
13
11
|
|
@@ -20,11 +18,16 @@ module MongoMapperExt
|
|
20
18
|
def generate_slug
|
21
19
|
return false if self[self.class.slug_key].blank?
|
22
20
|
max_length = self.class.slug_options[:max_length]
|
21
|
+
min_length = self.class.slug_options[:min_length] || 0
|
23
22
|
|
24
23
|
slug = self[self.class.slug_key].parameterize.to_s
|
25
24
|
slug = slug[0, max_length] if max_length
|
26
25
|
|
27
|
-
if
|
26
|
+
if slug.size < min_length
|
27
|
+
slug = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
if slug && !self.class.slug_options[:unique]
|
28
31
|
key = UUIDTools::UUID.random_create.hexdigest[0,4] #optimize
|
29
32
|
self.slug = key+"-"+slug
|
30
33
|
else
|
@@ -35,6 +38,14 @@ module MongoMapperExt
|
|
35
38
|
module ClassMethods
|
36
39
|
def slug_key(key = :name, options = {})
|
37
40
|
@slug_options ||= options
|
41
|
+
@@callback_type ||= begin
|
42
|
+
type = options[:callback_type] || :before_validation_on_create
|
43
|
+
|
44
|
+
send(type, :generate_slug)
|
45
|
+
|
46
|
+
type
|
47
|
+
end
|
48
|
+
|
38
49
|
@slug_key ||= key
|
39
50
|
end
|
40
51
|
class_eval do
|
@@ -3,83 +3,144 @@ module MongoMapperExt
|
|
3
3
|
def self.included(model)
|
4
4
|
model.class_eval do
|
5
5
|
extend ClassMethods
|
6
|
-
|
6
|
+
|
7
|
+
validate :add_mm_storage_errors
|
8
|
+
file_list :file_list
|
7
9
|
end
|
8
10
|
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# :metadata => metadata.deep_merge({:_id => self.id})
|
14
|
-
GridFS::GridStore.open(self.class.database, filename, "w",
|
15
|
-
:root => self.collection.name,
|
16
|
-
:metadata => {:_id => self.id}) do |f|
|
17
|
-
while data = io.read(256)
|
18
|
-
f.write(data)
|
19
|
-
end
|
20
|
-
io.close
|
21
|
-
end
|
22
|
-
else
|
23
|
-
(@_pending_files ||= {})[filename] = io
|
24
|
-
end
|
12
|
+
def put_file(name, io, options = {})
|
13
|
+
file_list = send(options.delete(:in) || :file_list)
|
14
|
+
file_list.put(name, io, options)
|
25
15
|
end
|
26
16
|
|
27
|
-
def fetch_file(
|
28
|
-
|
29
|
-
|
30
|
-
end
|
17
|
+
def fetch_file(name, options = {})
|
18
|
+
file_list = send(options.delete(:in) || :file_list)
|
19
|
+
file_list.get(name)
|
31
20
|
end
|
32
21
|
|
33
|
-
def files
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
else
|
38
|
-
finder = MongoMapper::Query
|
39
|
-
end
|
22
|
+
def files(options = {})
|
23
|
+
file_list = send(options.delete(:in) || :file_list)
|
24
|
+
file_list.files
|
25
|
+
end
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
@files = coll.find(criteria, options).map do |a|
|
44
|
-
MongoMapperExt::File.new(self, a)
|
45
|
-
end
|
27
|
+
def mm_storage_errors
|
28
|
+
@mm_storage_errors ||= {}
|
46
29
|
end
|
47
30
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
put_file(filename, data)
|
31
|
+
def add_mm_storage_errors
|
32
|
+
mm_storage_errors.each do |k, msgs|
|
33
|
+
msgs.each do |msg|
|
34
|
+
self.errors.add(k, msg)
|
53
35
|
end
|
54
|
-
@_pending_files = nil
|
55
36
|
end
|
56
37
|
end
|
57
38
|
|
58
39
|
module ClassMethods
|
59
|
-
def
|
60
|
-
|
40
|
+
def gridfs
|
41
|
+
@gridfs ||= Mongo::Grid.new(self.database)
|
42
|
+
end
|
43
|
+
|
44
|
+
def file_list(name)
|
45
|
+
key name, MongoMapperExt::FileList
|
46
|
+
define_method(name) do
|
47
|
+
list = self[name]
|
48
|
+
list.parent_document = self
|
49
|
+
list
|
50
|
+
end
|
51
|
+
|
52
|
+
after_create do |doc|
|
53
|
+
doc.send(name).sync_files
|
54
|
+
doc.save(:validate => false)
|
55
|
+
end
|
56
|
+
|
57
|
+
before_destroy do |doc|
|
58
|
+
doc.send(name).destroy_files
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def file_key(name, opts = {})
|
63
|
+
opts[:in] ||= :file_list
|
64
|
+
|
61
65
|
define_method("#{name}=") do |file|
|
62
|
-
|
63
|
-
|
66
|
+
if opts[:max_length] && file.respond_to?(:size) && file.size > opts[:max_length]
|
67
|
+
errors.add(name, I18n.t("mongomapper_ext.storage.errors.max_length", :default => "file is too long. max length is #{opts[:max_length]} bytes"))
|
68
|
+
end
|
64
69
|
|
65
|
-
if
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
if cb = opts[:validate]
|
71
|
+
if cb.kind_of?(Symbol)
|
72
|
+
send(opts[:validate], file)
|
73
|
+
elsif cb.kind_of?(Proc)
|
74
|
+
cb.call(file)
|
75
|
+
end
|
69
76
|
end
|
70
77
|
|
71
|
-
|
72
|
-
|
78
|
+
if self.errors.on(name).blank?
|
79
|
+
send(opts[:in]).get(name.to_s).put(name.to_s, file)
|
80
|
+
else
|
81
|
+
# we store the errors here because we want to validate before storing the file
|
82
|
+
mm_storage_errors.merge!(errors.errors)
|
83
|
+
end
|
73
84
|
end
|
74
85
|
|
75
86
|
define_method(name) do
|
76
|
-
|
87
|
+
send(opts[:in]).get(name.to_s)
|
77
88
|
end
|
78
89
|
|
79
90
|
define_method("has_#{name}?") do
|
80
|
-
|
91
|
+
send(opts[:in]).has_key?(name.to_s)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# NOTE: this method will be removed on next release
|
96
|
+
def upgrade_file_keys(*keys)
|
97
|
+
cname = self.collection_name
|
98
|
+
|
99
|
+
self.find_each do |object|
|
100
|
+
keys.each do |key|
|
101
|
+
object.upgrade_file_key(key, false)
|
102
|
+
end
|
103
|
+
|
104
|
+
object.save(:validate => false)
|
105
|
+
end
|
106
|
+
|
107
|
+
self.database.drop_collection(cname+".files")
|
108
|
+
self.database.drop_collection(cname+".chunks")
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
end
|
113
|
+
|
114
|
+
# NOTE: this method will be removed on next release
|
115
|
+
def upgrade_file_key(key, save = true)
|
116
|
+
cname = self.collection.name
|
117
|
+
|
118
|
+
files = self.database["#{cname}.files"]
|
119
|
+
chunks = self.database["#{cname}.chunks"]
|
120
|
+
|
121
|
+
fname = self["_#{key}"] rescue nil
|
122
|
+
return if fname.blank?
|
123
|
+
|
124
|
+
begin
|
125
|
+
n = Mongo::GridIO.new(files, chunks, fname, "r", :query => {:filename => fname})
|
126
|
+
|
127
|
+
v = n.read
|
128
|
+
|
129
|
+
if !v.empty?
|
130
|
+
data = StringIO.new(v)
|
131
|
+
self.put_file(key, data)
|
132
|
+
self["_#{key}"] = nil
|
133
|
+
|
134
|
+
self.save(:validate => false) if save
|
81
135
|
end
|
136
|
+
rescue => e
|
137
|
+
puts "ERROR: #{e}"
|
138
|
+
puts e.backtrace.join("\t\n")
|
139
|
+
return
|
82
140
|
end
|
141
|
+
|
142
|
+
files.remove(:_id => fname)
|
143
|
+
chunks.remove(:_id => fname)
|
83
144
|
end
|
84
145
|
end
|
85
146
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Translation < String
|
2
|
+
attr_accessor :keys
|
3
|
+
|
4
|
+
def initialize(*args)
|
5
|
+
super
|
6
|
+
@keys = {}
|
7
|
+
@keys["default"] = "en"
|
8
|
+
end
|
9
|
+
|
10
|
+
def []=(lang, text)
|
11
|
+
@keys[lang.to_s] = text
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](lang)
|
15
|
+
@keys[lang.to_s]
|
16
|
+
end
|
17
|
+
|
18
|
+
def languages
|
19
|
+
langs = @keys.keys
|
20
|
+
langs.delete("default")
|
21
|
+
langs
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_language=(lang)
|
25
|
+
@keys["default"] = lang
|
26
|
+
self.replace(@keys[lang.to_s])
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build(keys, default = "en")
|
30
|
+
tr = self.new
|
31
|
+
tr.keys = keys
|
32
|
+
tr.default_language = default
|
33
|
+
tr
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.to_mongo(value)
|
37
|
+
return value.keys if value.kind_of?(self)
|
38
|
+
|
39
|
+
@keys
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.from_mongo(value)
|
43
|
+
return value if value.kind_of?(self)
|
44
|
+
|
45
|
+
result = self.new
|
46
|
+
result.keys = value
|
47
|
+
result.default_language = value["default"] || "en"
|
48
|
+
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
data/lib/mongomapper_ext.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
2
|
|
3
|
-
|
3
|
+
if RUBY_VERSION =~ /^1\.8/
|
4
|
+
$KCODE = 'u'
|
5
|
+
end
|
4
6
|
|
5
7
|
require 'mongo_mapper'
|
6
8
|
require 'mongo/gridfs'
|
7
9
|
require 'uuidtools'
|
8
10
|
require 'active_support/inflector'
|
9
11
|
|
12
|
+
begin
|
13
|
+
require 'magic'
|
14
|
+
rescue LoadError
|
15
|
+
$stderr.puts "disabling `magic` support. use 'gem install magic' to enable it"
|
16
|
+
end
|
17
|
+
|
10
18
|
# types
|
11
19
|
require 'mongomapper_ext/types/open_struct'
|
12
20
|
require 'mongomapper_ext/types/timestamp'
|
13
21
|
|
14
22
|
# storage
|
23
|
+
require 'mongomapper_ext/file_list'
|
15
24
|
require 'mongomapper_ext/file'
|
16
25
|
require 'mongomapper_ext/storage'
|
17
26
|
|
data/mongomapper_ext.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mongomapper_ext}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David A. Cuadrado"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-04-11}
|
13
13
|
s.description = %q{MongoMapper extensions}
|
14
14
|
s.email = %q{krawek@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"VERSION",
|
26
26
|
"examples/filter.rb",
|
27
27
|
"examples/helper.rb",
|
28
|
+
"examples/i18n.rb",
|
28
29
|
"examples/slugizer.rb",
|
29
30
|
"examples/storage.rb",
|
30
31
|
"examples/tags.rb",
|
@@ -32,6 +33,7 @@ Gem::Specification.new do |s|
|
|
32
33
|
"examples/update.rb",
|
33
34
|
"lib/mongomapper_ext.rb",
|
34
35
|
"lib/mongomapper_ext/file.rb",
|
36
|
+
"lib/mongomapper_ext/file_list.rb",
|
35
37
|
"lib/mongomapper_ext/filter.rb",
|
36
38
|
"lib/mongomapper_ext/js/find_tags.js",
|
37
39
|
"lib/mongomapper_ext/js/tag_cloud.js",
|
@@ -40,6 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
"lib/mongomapper_ext/tags.rb",
|
41
43
|
"lib/mongomapper_ext/types/open_struct.rb",
|
42
44
|
"lib/mongomapper_ext/types/timestamp.rb",
|
45
|
+
"lib/mongomapper_ext/types/translation.rb",
|
43
46
|
"lib/mongomapper_ext/update.rb",
|
44
47
|
"mongomapper_ext.gemspec",
|
45
48
|
"test/helper.rb",
|
@@ -77,6 +80,7 @@ Gem::Specification.new do |s|
|
|
77
80
|
"examples/tags.rb",
|
78
81
|
"examples/storage.rb",
|
79
82
|
"examples/helper.rb",
|
83
|
+
"examples/i18n.rb",
|
80
84
|
"examples/slugizer.rb"
|
81
85
|
]
|
82
86
|
|
@@ -85,20 +89,20 @@ Gem::Specification.new do |s|
|
|
85
89
|
s.specification_version = 3
|
86
90
|
|
87
91
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
88
|
-
s.add_runtime_dependency(%q<mongo_mapper>, [">= 0.
|
92
|
+
s.add_runtime_dependency(%q<mongo_mapper>, [">= 0.7.3"])
|
89
93
|
s.add_runtime_dependency(%q<uuidtools>, [">= 2.0.0"])
|
90
94
|
s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
|
91
95
|
s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
92
96
|
s.add_development_dependency(%q<mocha>, [">= 0.9.4"])
|
93
97
|
else
|
94
|
-
s.add_dependency(%q<mongo_mapper>, [">= 0.
|
98
|
+
s.add_dependency(%q<mongo_mapper>, [">= 0.7.3"])
|
95
99
|
s.add_dependency(%q<uuidtools>, [">= 2.0.0"])
|
96
100
|
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
97
101
|
s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
98
102
|
s.add_dependency(%q<mocha>, [">= 0.9.4"])
|
99
103
|
end
|
100
104
|
else
|
101
|
-
s.add_dependency(%q<mongo_mapper>, [">= 0.
|
105
|
+
s.add_dependency(%q<mongo_mapper>, [">= 0.7.3"])
|
102
106
|
s.add_dependency(%q<uuidtools>, [">= 2.0.0"])
|
103
107
|
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
104
108
|
s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
data/test/models.rb
CHANGED
@@ -25,6 +25,9 @@ class Avatar # for Storage and File
|
|
25
25
|
include MongoMapperExt::Storage
|
26
26
|
|
27
27
|
file_key :data
|
28
|
+
|
29
|
+
file_list :alternatives
|
30
|
+
file_key :first_alternative, :in => :alternatives
|
28
31
|
end
|
29
32
|
|
30
33
|
class UserConfig #for OpenStruct
|
@@ -39,7 +42,7 @@ class BlogPost # for Slug and Filter
|
|
39
42
|
include MongoMapperExt::Tags
|
40
43
|
|
41
44
|
filterable_keys :title, :body, :tags, :date
|
42
|
-
slug_key :title, :max_length => 18
|
45
|
+
slug_key :title, :max_length => 18, :min_length => 3, :callback_type => :before_validation
|
43
46
|
language :find_language
|
44
47
|
|
45
48
|
key :title, String
|
data/test/test_slugizer.rb
CHANGED
@@ -31,6 +31,21 @@ class TestSlugizer < Test::Unit::TestCase
|
|
31
31
|
:body => "HeRe is tHe Body of the bLog pOsT")
|
32
32
|
@blogpost.slug.should =~ /\w+-ultimo-video-canci/
|
33
33
|
end
|
34
|
+
|
35
|
+
should "not accept slugs with length < :min_length" do
|
36
|
+
@blogpost = BlogPost.create(:title => "a",
|
37
|
+
:body => "HeRe is tHe Body of the bLog pOsT")
|
38
|
+
@blogpost.slug.should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
should "update the slug after updating the object" do
|
42
|
+
@blogpost = BlogPost.create(:title => "ultimo video/cancion en youtube?",
|
43
|
+
:body => "HeRe is tHe Body of the bLog pOsT")
|
44
|
+
@blogpost.slug.should =~ /\w+-ultimo-video-canci/
|
45
|
+
@blogpost.title = "primer video/cancion en youtube?"
|
46
|
+
@blogpost.valid?
|
47
|
+
@blogpost.slug.should =~ /\w+-primer-video-canci/
|
48
|
+
end
|
34
49
|
end
|
35
50
|
|
36
51
|
context "finding objects" do
|
data/test/test_storage.rb
CHANGED
@@ -9,13 +9,15 @@ class StorageTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
should "store the file" do
|
11
11
|
@avatar.put_file("an_avatar.png", @data)
|
12
|
-
|
12
|
+
@avatar.save
|
13
|
+
avatar = Avatar.find(@avatar.id)
|
14
|
+
data = avatar.fetch_file("an_avatar.png").read
|
13
15
|
data.should == "my avatar image"
|
14
16
|
end
|
15
17
|
|
16
|
-
should "close the file after storing" do
|
18
|
+
should "not close the file after storing" do
|
17
19
|
@avatar.put_file("an_avatar.png", @data)
|
18
|
-
@data.
|
20
|
+
@data.should_not be_closed
|
19
21
|
end
|
20
22
|
|
21
23
|
context "in attributes" do
|
@@ -38,9 +40,34 @@ class StorageTest < Test::Unit::TestCase
|
|
38
40
|
@avatar.fetch_file("an_avatar.png").read.should == "my avatar image"
|
39
41
|
end
|
40
42
|
|
41
|
-
should "store
|
43
|
+
should "not store the file if object is new" do
|
42
44
|
@avatar.put_file("an_avatar.png", @data)
|
43
|
-
@avatar.fetch_file("an_avatar.png").should
|
45
|
+
lambda {@avatar.fetch_file("an_avatar.png").read}.should raise_error
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "with lists" do
|
50
|
+
setup do
|
51
|
+
@avatar = Avatar.new
|
52
|
+
@alternative = File.new(__FILE__)
|
53
|
+
@data = File.read(__FILE__)
|
54
|
+
end
|
55
|
+
teardown do
|
56
|
+
@alternative.close
|
57
|
+
end
|
58
|
+
|
59
|
+
should "store the file" do
|
60
|
+
@avatar.first_alternative = @alternative
|
61
|
+
@avatar.save
|
62
|
+
fromdb = @avatar.reload
|
63
|
+
fromdb.first_alternative.read.should == @data
|
64
|
+
end
|
65
|
+
|
66
|
+
should "store the file in the alternative list" do
|
67
|
+
@avatar.alternatives.put("an_alternative", @alternative)
|
68
|
+
@avatar.save
|
69
|
+
@avatar.reload
|
70
|
+
@avatar.alternatives.get("an_alternative").read.should == @data
|
44
71
|
end
|
45
72
|
end
|
46
73
|
end
|
data/test/test_tags.rb
CHANGED
@@ -37,8 +37,9 @@ class TestTags < Test::Unit::TestCase
|
|
37
37
|
|
38
38
|
should "find tags that start with li" do
|
39
39
|
tags = BlogPost.find_tags(/^li/)
|
40
|
-
|
41
|
-
|
40
|
+
[{"name"=>"list", "count"=>2.0}, {"name"=>"linux", "count"=>1.0}].each do |entry|
|
41
|
+
tags.should include(entry)
|
42
|
+
end
|
42
43
|
tags.size.should == 2
|
43
44
|
end
|
44
45
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- David A. Cuadrado
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-04-11 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -26,9 +26,9 @@ dependencies:
|
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
segments:
|
28
28
|
- 0
|
29
|
-
-
|
30
|
-
-
|
31
|
-
version: 0.
|
29
|
+
- 7
|
30
|
+
- 3
|
31
|
+
version: 0.7.3
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
@@ -105,6 +105,7 @@ files:
|
|
105
105
|
- VERSION
|
106
106
|
- examples/filter.rb
|
107
107
|
- examples/helper.rb
|
108
|
+
- examples/i18n.rb
|
108
109
|
- examples/slugizer.rb
|
109
110
|
- examples/storage.rb
|
110
111
|
- examples/tags.rb
|
@@ -112,6 +113,7 @@ files:
|
|
112
113
|
- examples/update.rb
|
113
114
|
- lib/mongomapper_ext.rb
|
114
115
|
- lib/mongomapper_ext/file.rb
|
116
|
+
- lib/mongomapper_ext/file_list.rb
|
115
117
|
- lib/mongomapper_ext/filter.rb
|
116
118
|
- lib/mongomapper_ext/js/find_tags.js
|
117
119
|
- lib/mongomapper_ext/js/tag_cloud.js
|
@@ -120,6 +122,7 @@ files:
|
|
120
122
|
- lib/mongomapper_ext/tags.rb
|
121
123
|
- lib/mongomapper_ext/types/open_struct.rb
|
122
124
|
- lib/mongomapper_ext/types/timestamp.rb
|
125
|
+
- lib/mongomapper_ext/types/translation.rb
|
123
126
|
- lib/mongomapper_ext/update.rb
|
124
127
|
- mongomapper_ext.gemspec
|
125
128
|
- test/helper.rb
|
@@ -181,4 +184,5 @@ test_files:
|
|
181
184
|
- examples/tags.rb
|
182
185
|
- examples/storage.rb
|
183
186
|
- examples/helper.rb
|
187
|
+
- examples/i18n.rb
|
184
188
|
- examples/slugizer.rb
|