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 +7 -0
- data/README.md +90 -0
- data/lib/generators/image_thread/install_generator.rb +22 -0
- data/lib/generators/image_thread/templates/create_image_thread_tables.rb +37 -0
- data/lib/image_thread/engine.rb +6 -0
- data/lib/image_thread/exceptions.rb +4 -0
- data/lib/image_thread/model_methods.rb +84 -0
- data/lib/image_thread/patches/form_helper.rb +37 -0
- data/lib/image_thread/uploaders/image_uploader.rb +68 -0
- data/lib/image_thread/version.rb +3 -0
- data/lib/image_thread.rb +29 -0
- metadata +153 -0
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,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
|
data/lib/image_thread.rb
ADDED
@@ -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:
|