associo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d303af488b234afa9287b1047241a993511df12f
4
+ data.tar.gz: 427468da4bec28be43c75d87559c6cf7c9115226
5
+ SHA512:
6
+ metadata.gz: e5272729031e9951c3150af06233b1a63ea1e830f06ae66a6a6eb089a76f24a57ca95489bcac85e4554f99e57996f7675426646e09b47243b45546d313d0e866
7
+ data.tar.gz: c19d6f2a7718d000403e763289ced24a0dc1d1adaa122961b1496023edd168890dd4a5e55dec6c6a8c10c47c11bb537adb925126362d20db74a5053f311d619b
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'bson_ext', :require => false
7
+ gem 'shoulda'
8
+ gem 'mocha'
9
+ gem 'jnunemaker-matchy'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ = DEAD AND UNMAINTAINED
2
+
3
+ == Joint
4
+
5
+ MongoMapper and GridFS joined in file upload love.
6
+
7
+ == Usage
8
+
9
+ Declare the plugin and use the attachment method to make attachments.
10
+
11
+ class Foo
12
+ include MongoMapper::Document
13
+ plugin Joint
14
+
15
+ attachment :image
16
+ attachment :pdf
17
+ end
18
+
19
+ This gives you #image, #image=, #pdf, and #pdf=. The = methods take any IO that responds to read (File, Tempfile, etc). The image and pdf methods return a GridIO instance (can be found in the ruby driver).
20
+
21
+ Also, #image and #pdf are proxies so you can do stuff like:
22
+
23
+ doc.image.id, doc.image.size, doc.image.type, doc.image.name
24
+
25
+ If you call a method other than those in the proxy it calls it on the GridIO instance so you can still get at all the GridIO instance methods.
26
+
27
+ == Note on Patches/Pull Requests
28
+
29
+ * Fork the project.
30
+ * Make your feature addition or bug fix.
31
+ * Add tests for it. This is important so I don't break it in a
32
+ future version unintentionally.
33
+ * Commit, do not mess with rakefile, version, or history.
34
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
35
+ * Send me a pull request. Bonus points for topic branches.
36
+
37
+ == Copyright
38
+
39
+ Copyright (c) 2010 John Nunemaker. See LICENSE for details.
@@ -0,0 +1,12 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.ruby_opts = ['-rubygems']
9
+ test.verbose = true
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../lib/associo/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'associo'
7
+ s.summary = %(MongoMapper and GridFS joined in file upload love.)
8
+ s.description = %(Implements an easy to use GridFS API for MongoMapper.)
9
+ s.email = 'syntruth@gmail.com'
10
+ s.homepage = 'https://github.com/syntruth/Associo'
11
+ s.authors = ['John Nunemaker', 'Randy Carnahan']
12
+ s.version = Associo::VERSION
13
+
14
+ s.add_dependency 'wand', '~> 0.4'
15
+ s.add_dependency 'mime-types'
16
+ s.add_dependency 'mongo_mapper', '~> 0.9'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f|
21
+ File.basename(f)
22
+ }
23
+
24
+ s.require_paths = ['lib']
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'set'
2
+ require 'mime/types'
3
+ require 'wand'
4
+ require 'active_support/concern'
5
+
6
+ # Main Associo module
7
+ module Associo
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :attachment_names
12
+
13
+ self.attachment_names = Set.new
14
+
15
+ include attachment_accessor_module
16
+ end
17
+
18
+ def self.blank?(str)
19
+ str.nil? || str !~ /\S/
20
+ end
21
+
22
+ private_class_method :blank?
23
+ end
24
+
25
+ require 'associo/class_methods'
26
+ require 'associo/instance_methods'
27
+ require 'associo/attachment_proxy'
28
+ require 'associo/io'
29
+ require 'associo/file_helpers'
@@ -0,0 +1,51 @@
1
+ module Associo
2
+ # Proxy for the Attached file.
3
+ class AttachmentProxy
4
+ def initialize(instance, name)
5
+ @instance = instance
6
+ @name = name
7
+ end
8
+
9
+ def id
10
+ @instance.send "#{@name}_id"
11
+ end
12
+
13
+ def name
14
+ @instance.send "#{@name}_name"
15
+ end
16
+
17
+ def size
18
+ @instance.send "#{@name}_size"
19
+ end
20
+
21
+ def type
22
+ @instance.send "#{@name}_type"
23
+ end
24
+
25
+ def chunk_size
26
+ @instance.send "#{@name}_chunk_size"
27
+ end
28
+
29
+ def nil?
30
+ !@instance.send("#{@name}?")
31
+ end
32
+
33
+ alias blank? nil?
34
+
35
+ def grid_io
36
+ @grid_io ||= @instance.grid.get(id)
37
+ end
38
+
39
+ def method_missing(method, *args, &block)
40
+ if grid_io.respond_to? method.to_s
41
+ grid_io.send(method, *args, &block)
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ def respond_to_missing?(method, include_private = false)
48
+ super
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,64 @@
1
+ module Associo
2
+ # Define new class methods for documents that
3
+ # include the Associo module.
4
+ module ClassMethods
5
+ DEFAULT_CHUNK_SIZE = 261_120
6
+
7
+ def attachment_accessor_module
8
+ @attachment_accessor_module ||= Module.new
9
+ end
10
+
11
+ # rubocop:disable AbcSize, MethodLength
12
+ # TODO: refactor this.
13
+ def attachment(name, options = {})
14
+ options.symbolize_keys!
15
+
16
+ name = name.to_sym
17
+
18
+ chunk_size = options.fetch(:chunk_size, DEFAULT_CHUNK_SIZE).to_i
19
+ chunk_size = DEFAULT_CHUNK_SIZE if chunk_size.zero?
20
+
21
+ self.attachment_names = attachment_names.dup.add(name)
22
+
23
+ after_save :save_attachments
24
+ before_save :nullify_nil_attachments_attributes
25
+ after_save :destroy_nil_attachments
26
+ before_destroy :destroy_all_attachments
27
+
28
+ key :"#{name}_id", ObjectId
29
+ key :"#{name}_name", String
30
+ key :"#{name}_size", Integer
31
+ key :"#{name}_type", String
32
+
33
+ # Allow for optional, custom chunk size, in bytes.
34
+ # Default size is 255k, set above.
35
+ key :"#{name}_chunk_size", Integer, default: chunk_size
36
+
37
+ validates_presence_of(name) if options[:required]
38
+
39
+ attachment_accessor_module.module_eval <<-EOC
40
+ def #{name}
41
+ @#{name} ||= AttachmentProxy.new(self, :#{name})
42
+ end
43
+
44
+ def #{name}?
45
+ !nil_attachments.has_key?(:#{name}) && send(:#{name}_id?)
46
+ end
47
+
48
+ def #{name}=(file)
49
+ if file.nil?
50
+ nil_attachments[:#{name}] = send("#{name}_id")
51
+ assigned_attachments.delete(:#{name})
52
+ else
53
+ send("#{name}_id=", BSON::ObjectId.new) if send("#{name}_id").nil?
54
+ send("#{name}_name=", Associo::FileHelpers.name(file))
55
+ send("#{name}_size=", Associo::FileHelpers.size(file))
56
+ send("#{name}_type=", Associo::FileHelpers.type(file))
57
+ assigned_attachments[:#{name}] = file
58
+ nil_attachments.delete(:#{name})
59
+ end
60
+ end
61
+ EOC
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,20 @@
1
+ module Associo
2
+ # Syntax helper for accessing file attributes.
3
+ module FileHelpers
4
+ def self.name(file)
5
+ return file.original_filename if file.respond_to?(:original_filename)
6
+
7
+ File.basename(file.path)
8
+ end
9
+
10
+ def self.size(file)
11
+ file.respond_to?(:size) ? file.size : File.size(file)
12
+ end
13
+
14
+ def self.type(file)
15
+ return file.type if file.is_a? Associo::IO
16
+
17
+ Wand.wave file.path, original_filename: Associo::FileHelpers.name(file)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,59 @@
1
+ # Define Instance Methods
2
+ module Associo
3
+ def grid
4
+ @grid ||= Mongo::Grid.new(database)
5
+ end
6
+
7
+ private
8
+
9
+ def assigned_attachments
10
+ @assigned_attachments ||= {}
11
+ end
12
+
13
+ def nil_attachments
14
+ @nil_attachments ||= {}
15
+ end
16
+
17
+ # IO must respond to read and rewind
18
+ def save_attachments
19
+ assigned_attachments.each_pair do |name, io|
20
+ next unless io.respond_to?(:read)
21
+
22
+ io.rewind if io.respond_to?(:rewind)
23
+
24
+ grid.delete send(name).id
25
+
26
+ grid.put io, save_attachment_hash(name)
27
+ end
28
+
29
+ assigned_attachments.clear
30
+ end
31
+
32
+ def save_attachment_hash(name)
33
+ {
34
+ _id: send(name).id,
35
+ filename: send(name).name,
36
+ content_type: send(name).type,
37
+ chunk_size: send(name).chunk_size
38
+ }
39
+ end
40
+
41
+ def nullify_nil_attachments_attributes
42
+ nil_attachments.each_key do |name|
43
+ send :"#{name}_id=", nil
44
+ send :"#{name}_size=", nil
45
+ send :"#{name}_type=", nil
46
+ send :"#{name}_name=", nil
47
+ end
48
+ end
49
+
50
+ def destroy_nil_attachments
51
+ nil_attachments.each_value { |id| grid.delete id }
52
+
53
+ nil_attachments.clear
54
+ end
55
+
56
+ def destroy_all_attachments
57
+ self.class.attachment_names.map { |name| grid.delete send(name).id }
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ require 'stringio'
2
+
3
+ module Associo
4
+ # Defines the file io for the attached file.
5
+ class IO
6
+ attr_accessor :name, :content, :type, :size
7
+
8
+ def initialize(attrs = {})
9
+ attrs.each { |key, value| send("#{key}=", value) }
10
+
11
+ @type ||= 'plain/text'
12
+ end
13
+
14
+ def content=(value)
15
+ @io = StringIO.new(value || nil)
16
+ @size = value ? value.size : 0
17
+ end
18
+
19
+ def read(*args)
20
+ @io.read(*args)
21
+ end
22
+
23
+ def rewind
24
+ @io.rewind if @io.respond_to?(:rewind)
25
+ end
26
+
27
+ alias path name
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Associo
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,50 @@
1
+ def run(cmd)
2
+ puts(cmd)
3
+
4
+ output = ''
5
+
6
+ IO.popen(cmd) do |com|
7
+ com.each_char do |c|
8
+ print c
9
+ output << c
10
+ $stdout.flush
11
+ end
12
+ end
13
+
14
+ output
15
+ end
16
+
17
+ def run_test_file(file)
18
+ run %(ruby -I"lib:test" -rubygems #{file})
19
+ end
20
+
21
+ def run_all_tests
22
+ run 'rake test'
23
+ end
24
+
25
+ def related_test_files(path)
26
+ Dir['test/**/*.rb'].select { |file| file =~ /test_#{File.basename(path)}/ }
27
+ end
28
+
29
+ watch('test/test_helper\.rb') do
30
+ system 'clear'
31
+ run_all_test
32
+ end
33
+
34
+ watch('test/.*test_.*\.rb') do |m|
35
+ system 'clear'
36
+ run_test_file m[0]
37
+ end
38
+
39
+ watch('lib/.*') do |m|
40
+ related_test_files(m[0]).each { |file| run_test_file(file) }
41
+ end
42
+
43
+ # Ctrl-\
44
+ Signal.trap('QUIT') do
45
+ puts " --- Running all tests ---\n\n"
46
+ run_all_tests
47
+ end
48
+
49
+ # Ctrl-C
50
+ Signal.trap('INT') { abort("\n") }