image_thread 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 930e6c0b738a5266a59c45cac3d594e51256caee
4
+ data.tar.gz: e3035e6bb80b3d4319d5083ac5c3986815f57767
5
+ SHA512:
6
+ metadata.gz: cf42af7b93564bfdc34535d0798f2281eb23daf371304471deb5ffd3b3d89b49f34b37b8e4f49f65a6a5a05ab809554051930d1f4f4913875fc2f308f841244b
7
+ data.tar.gz: f4cec8f27c0113a48db881e0b25af1f0b23001898a1841f7bfc7fca9e373bcfb9b9ce1417b6195c84da09d75e4e7c1882bbd52cea3a76fb2a9231f0ee9e6d1d9
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # ImageThread
2
+
3
+ Gem for multiply file upload (for backend).
4
+
5
+ One thread (thread of images) have many images. Attach thread to your model and
6
+ you get "album"
7
+
8
+ Dependencies:
9
+ 1. carrierwave
10
+ 2. foreigner
11
+
12
+ ## TODOs:
13
+ 1. Single/Multi uploaders
14
+ 2. Validators - size, count, format and etc
15
+ 3. Handle errors
16
+ 4. Customize uploaders
17
+ 5. I18n
18
+ 6. Reorder images in thread
19
+ 7. Placeholder generator for nonexistent files
20
+
21
+ ## Features
22
+ 1. Dynamic thumbs (i.e. thread.image[0].thumb('120x120') or thread.image[0].thumb('300x100!'))
23
+ 2. Settings: remove file on destroy(ex.: has_image_thread :images, delete: :file )
24
+
25
+ ## Examples
26
+
27
+ ## Installation
28
+
29
+ Add this line to your application's Gemfile:
30
+
31
+ gem 'image_thread'
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install image_thread
40
+
41
+ Copy migrations:
42
+
43
+ rails generate image_thread:install
44
+
45
+ Then run this migration.
46
+
47
+ ## Usage
48
+
49
+ ### Setup client-side
50
+ Add js to your manifest file
51
+
52
+ require image_thread
53
+
54
+ If you use custom class for uploader (default: '*.image_thread_fileupload*') require only uploader without init file
55
+
56
+ require image_thread/uploader
57
+
58
+ and init uploader(s):
59
+
60
+ $(function(){
61
+ $('#custom-selector').each(function(){
62
+ $(this).fileupload({
63
+ formData: {
64
+ uploader: $(this).data('uploader'),
65
+ thread: $(this).data('thread'),
66
+ dir: $(this).data('dir')
67
+ },
68
+ inputName: $(this).data('name'),
69
+ previewCrop: true,
70
+ previewMaxWidth: 120,
71
+ previewMaxHeight: 120,
72
+ filesContainer: $(this).closest('.uploader-container').find('.files')
73
+ });
74
+ });
75
+ });
76
+
77
+ ### Add thread to model
78
+ 1. Create filed %{your_thread_name}_id in your model (Link to thread)
79
+ 2. Inside model class add line: has_image_thread :%{your_thread_name}
80
+
81
+ ### Upload files
82
+ 1. In form use this: image_thread_field helper(like text_field etc): form.image_thread_field(:%{your_thread_name}, options)
83
+
84
+ ## Contributing
85
+
86
+ 1. Fork it ( http://github.com/<my-github-username>/image_thread/fork )
87
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
88
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
89
+ 4. Push to the branch (`git push origin my-new-feature`)
90
+ 5. Create new Pull Request
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record'
4
+
5
+ module ImageThread
6
+ class InstallGenerator < ::Rails::Generators::Base
7
+ include ::Rails::Generators::Migration
8
+
9
+ source_root File.expand_path('../templates', __FILE__)
10
+ class_option :with_changes, :type => :boolean, :default => false, :desc => "Store changeset (diff) with each version"
11
+
12
+ desc 'Generates (but does not run) a migration to add image thread tables.'
13
+
14
+ def create_migration_file
15
+ migration_template 'create_image_thread_tables.rb', 'db/migrate/create_image_thread_tables.rb'
16
+ end
17
+
18
+ def self.next_migration_number(dirname)
19
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ class CreateImageThreadTables < ActiveRecord::Migration
2
+ def up
3
+ create_table :image_thread_threads do |t|
4
+ t.references :default_image, default: nil, comment: 'Reference to default image of thread'
5
+ t.references :owner, comment: 'Thread owner'
6
+ t.datetime :created_at
7
+ end
8
+
9
+ add_index :image_thread_threads, :default_image_id
10
+
11
+ create_table :image_thread_images do |t|
12
+ t.references :thread
13
+ t.string :name, comment: 'Displayed file name'
14
+ t.string :source, null: false, comment: 'Real file name'
15
+ t.string :state, default: 'new', null: false, comment: 'File state: active, new, deleted, archive'
16
+ t.integer :order_pos, default: 0, comment: 'Order position'
17
+ t.string :dir, default: ''
18
+ t.datetime :created_at
19
+ end
20
+
21
+ add_index :image_thread_images, :thread_id
22
+
23
+ add_foreign_key :image_thread_threads, :image_thread_images, column: :default_image_id, dependent: :nullify
24
+ add_foreign_key :image_thread_images, :image_thread_threads, column: :thread_id, dependent: :nullify
25
+ end
26
+
27
+ def down
28
+ remove_foreign_key :image_thread_threads, column: :default_image_id
29
+ remove_foreign_key :image_thread_images, column: :thread_id
30
+
31
+ remove_index :image_thread_threads, :default_image_id
32
+ remove_index :image_thread_images, :thread_id
33
+
34
+ drop_table :image_thread_images
35
+ drop_table :image_thread_threads
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ module ImageThread
2
+ class Engine < ::Rails::Engine
3
+ config.autoload_paths += %W(#{config.root}/lib/**/)
4
+ config.assets.paths += %W(#{config.root}/vendor/assets/fonts/)
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module ImageThread
2
+ class DifferentThreads < StandardError; end
3
+ class WrongProcessMethod < StandardError; end
4
+ end
@@ -0,0 +1,84 @@
1
+ module ImageThread
2
+ module ModelMethods
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def has_image_thread(field, opt = {})
10
+ field_id = [field, 'id'].join('_')
11
+ before_save_method = ['append_images', field].join('_')
12
+
13
+ # Options
14
+ delete = opt[:delete] || [] # :row, :files
15
+
16
+
17
+ class_eval do
18
+ belongs_to field, class_name: 'ImageThread::Thread'
19
+ before_save before_save_method.to_sym
20
+
21
+ define_method before_save_method do
22
+ thread_id = nil
23
+ image_ids = instance_variable_get(:"@#{field}_images").map { |i| i[:id] }
24
+
25
+ ImageThread::Image.select('id, thread_id').where(id: image_ids).each do |image|
26
+ if !thread_id.blank? && thread_id != image.thread_id
27
+ raise DifferentThreads, "Try to save images from different thread as one. Image ids: #{image_ids}"
28
+ end
29
+
30
+ thread_id = image.thread_id
31
+ end
32
+
33
+ #TODO: Deactivate images
34
+
35
+ transaction do
36
+ instance_variable_get(:"@#{field}_images").each do |image|
37
+ image = ImageThread::Image.find image[:id]
38
+
39
+ unless image.blank?
40
+ image.update(state: image[:state]) unless image[:state].blank?
41
+
42
+ if state == ImageThread::Image::STATE_DELETED
43
+ # Remove all files
44
+ if delete.include?(:files)
45
+ dir_name = File.dirname(image.source.path)
46
+ file_name = File.basename(image.source.path)
47
+
48
+ Dir.chdir(dir_name)
49
+ Dir.glob(['*', file_name].join).each do |file|
50
+ File.delete File.expand_path(file)
51
+ end
52
+
53
+ File.expand_path image.source
54
+ end
55
+
56
+ # Remove row from DB
57
+ image.destroy if delete.include?(:row) || delete.include?(:files)
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ _assign_attribute(field_id, thread_id)
65
+ instance_variable_set(:"@#{field}_images", [])
66
+ end
67
+
68
+ define_method [field, 'images='].join('_') do |images|
69
+ res = []
70
+ images.each do |image|
71
+ id, state = image.split(':')
72
+ res << {id: id, state: state}
73
+ end
74
+ instance_variable_set(:"@#{field}_images", res)
75
+ end
76
+
77
+ define_method [field, 'images'].join('_') do
78
+ instance_variable_get(:"@#{field}_images")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module ActionView
2
+ module Helpers
3
+ module FormHelper
4
+ def image_thread_field(object_name, method, options = nil)
5
+ tag = ActionView::Helpers::Tags::Base.new(object_name, [method, 'images'].join('_'), self)
6
+ thread = options.delete(:thread)
7
+
8
+ options.update(class: 'image_thread_fileupload')
9
+ options.update(data: { url: '/image_thread/images',
10
+ uploader: [object_name, method, 'uploader', SecureRandom.hex(16)].join('_'),
11
+ thread: thread.to_i,
12
+ dir: options[:dir],
13
+ name: tag.send(:tag_name, true) })
14
+
15
+ template = <<-HTML
16
+ <div class="uploader-container">
17
+ <span class="btn btn-success fileinput-button">
18
+ <span class="glyphicon glyphicon-plus"> Add files</span>
19
+ #{file_field_tag(:source, options)}
20
+ </span>
21
+ <div class="files uploader-#{method} clearfix">#{render_image_thread(thread, tag.send(:tag_name, true))}</div>
22
+ </div>
23
+ HTML
24
+
25
+ template.html_safe
26
+ end
27
+ end
28
+
29
+ class FormBuilder
30
+ def image_thread_field(method, options = {})
31
+ options[:thread] = self.object.send([method, 'id'].join('_'))
32
+ options[:dir] = (options[:dir] || self.object.class.to_s).parameterize('_')
33
+ @template.image_thread_field(@object_name, method, options)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ module ImageThread
2
+ module Uploaders
3
+ class ImageUploader < ::CarrierWave::Uploader::Base
4
+ include CarrierWave::MiniMagick
5
+
6
+ attr_writer :name
7
+
8
+ process resize_to_limit: [2000, 2000]
9
+ process convert: :jpg
10
+
11
+
12
+ def thumb(size)
13
+ uploader = Class.new(self.class)
14
+ ::CarrierWave::Uploader.const_set("Uploader#{uploader.object_id}".gsub('-', '_'), uploader)
15
+
16
+ uploader.versions.clear
17
+ uploader.version_names = [size]
18
+
19
+ img = uploader.new(self.model)
20
+ img.retrieve_from_store!(self.file.identifier)
21
+ cached = File.join(CarrierWave.root, img.url)
22
+
23
+ unless File.exist?(cached)
24
+ file.copy_to(cached)
25
+
26
+ size = size.split('x').map(&:to_i)
27
+ resize = case size
28
+ when /!$/ then :resize_to_fit
29
+ when /\#$/ then :resize_to_limit
30
+ else :resize_to_fill
31
+ end
32
+
33
+ img.send resize, *size
34
+ img.name = file.original_filename
35
+ img.store!
36
+ end
37
+
38
+ img
39
+ end
40
+
41
+ def extension_white_list
42
+ %w(jpg jpeg gif png)
43
+ end
44
+
45
+ def filename
46
+ @name ||= "#{secure_token(25)}.jpg" if original_filename.present?
47
+ end
48
+
49
+ def store_dir
50
+ dir = model.dir || 'all'
51
+
52
+ ['uploads', 'images', dir, model.thread_id].join('/')
53
+ end
54
+
55
+ def move_to_store
56
+ true
57
+ end
58
+
59
+ protected
60
+
61
+ def secure_token(length = 20)
62
+ var = :"@#{mounted_as}_secure_token"
63
+ model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.hex(length/2))
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ module ImageThread
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'rails/all'
2
+
3
+ require 'carrierwave'
4
+ require 'foreigner'
5
+ require 'migration_comments'
6
+ require 'image_thread/patches/form_helper'
7
+ require 'image_thread/model_methods'
8
+
9
+ module ImageThread
10
+ extend ActiveSupport::Autoload
11
+
12
+ mattr_accessor :image_table_name
13
+ @@image_table_name = :image_thread_images
14
+
15
+ mattr_accessor :thread_table_name
16
+ @@thread_table_name = :image_thread_threads
17
+
18
+ def self.setup
19
+ yield self
20
+ end
21
+ end
22
+
23
+ ActiveSupport.on_load(:active_record) do
24
+ include ImageThread::ModelMethods
25
+ end
26
+
27
+ require 'image_thread/exceptions'
28
+ require 'image_thread/version'
29
+ require 'image_thread/engine'
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_thread
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Arthur Shcheglov(fc_arny)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 4.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 4.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: carrierwave
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mini_magick
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: foreigner
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: migration_comments
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Images for models
112
+ email:
113
+ - arthur.shcheglov@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - README.md
119
+ - lib/generators/image_thread/install_generator.rb
120
+ - lib/generators/image_thread/templates/create_image_thread_tables.rb
121
+ - lib/image_thread.rb
122
+ - lib/image_thread/engine.rb
123
+ - lib/image_thread/exceptions.rb
124
+ - lib/image_thread/model_methods.rb
125
+ - lib/image_thread/patches/form_helper.rb
126
+ - lib/image_thread/uploaders/image_uploader.rb
127
+ - lib/image_thread/version.rb
128
+ homepage: http://martsoft.ru/soft/image_thread
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 2.2.2
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Images for models
152
+ test_files: []
153
+ has_rdoc: