dynamic_image 3.0.9 → 3.1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +393 -67
  3. data/app/models/dynamic_image/variant.rb +10 -0
  4. data/lib/dynamic_image/backfill.rb +85 -0
  5. data/lib/dynamic_image/belongs_to.rb +22 -0
  6. data/lib/dynamic_image/breakpoints.rb +93 -0
  7. data/lib/dynamic_image/controller.rb +51 -7
  8. data/lib/dynamic_image/digest_verifier.rb +20 -9
  9. data/lib/dynamic_image/engine.rb +3 -0
  10. data/lib/dynamic_image/errors.rb +30 -0
  11. data/lib/dynamic_image/format.rb +149 -8
  12. data/lib/dynamic_image/format_negotiator.rb +84 -0
  13. data/lib/dynamic_image/helper/formats.rb +57 -0
  14. data/lib/dynamic_image/helper/pictures.rb +124 -0
  15. data/lib/dynamic_image/helper.rb +100 -60
  16. data/lib/dynamic_image/image_processor/colors.rb +3 -7
  17. data/lib/dynamic_image/image_processor/frames.rb +10 -1
  18. data/lib/dynamic_image/image_processor/transform.rb +17 -2
  19. data/lib/dynamic_image/image_processor.rb +33 -6
  20. data/lib/dynamic_image/image_reader.rb +19 -1
  21. data/lib/dynamic_image/image_sizing.rb +51 -19
  22. data/lib/dynamic_image/metadata.rb +56 -5
  23. data/lib/dynamic_image/model/dimensions.rb +32 -13
  24. data/lib/dynamic_image/model/transformations.rb +47 -11
  25. data/lib/dynamic_image/model/validations.rb +15 -8
  26. data/lib/dynamic_image/model/variants.rb +6 -3
  27. data/lib/dynamic_image/model.rb +58 -34
  28. data/lib/dynamic_image/picture/format_policy.rb +72 -0
  29. data/lib/dynamic_image/picture.rb +215 -0
  30. data/lib/dynamic_image/processed_image.rb +48 -14
  31. data/lib/dynamic_image/ratio.rb +43 -0
  32. data/lib/dynamic_image/routing.rb +14 -3
  33. data/lib/dynamic_image/schema.rb +47 -0
  34. data/lib/dynamic_image/version.rb +1 -1
  35. data/lib/dynamic_image.rb +61 -1
  36. data/lib/rails/generators/dynamic_image/resource/USAGE +17 -0
  37. data/lib/rails/generators/dynamic_image/resource/resource_generator.rb +57 -21
  38. data/lib/rails/generators/dynamic_image/resource/templates/create_table_migration.rb.tt +32 -0
  39. data/lib/rails/generators/dynamic_image/upgrade/USAGE +17 -0
  40. data/lib/rails/generators/dynamic_image/upgrade/templates/upgrade_migration.rb.tt +10 -0
  41. data/lib/rails/generators/dynamic_image/upgrade/upgrade_generator.rb +111 -0
  42. data/lib/tasks/dynamic_image.rake +25 -0
  43. metadata +23 -12
  44. data/lib/dynamic_image/jobs/create_variant.rb +0 -22
  45. data/lib/dynamic_image/jobs.rb +0 -3
@@ -2,22 +2,60 @@
2
2
 
3
3
  require "rails/generators"
4
4
  require "rails/generators/rails/resource/resource_generator"
5
+ require "rails/generators/active_record/migration"
6
+
7
+ require "dynamic_image/schema"
5
8
 
6
9
  module DynamicImage
10
+ # Generators for setting up DynamicImage in an application.
7
11
  module Generators
12
+ # Creates a DynamicImage resource: a model including {DynamicImage::Model}, a controller including
13
+ # {DynamicImage::Controller}, a migration with the columns DynamicImage needs, and an +image_resources+ route.
14
+ #
15
+ # bin/rails generate dynamic_image:resource image
16
+ #
17
+ # Additional attributes are passed through to the migration, letting a resource carry fields of its own.
18
+ #
19
+ # bin/rails generate dynamic_image:resource photo caption:string
20
+ #
21
+ # The migration is rendered from {DynamicImage::Schema}. The Active Record generator can't express nullability
22
+ # or indexes through its +name:type+ arguments.
8
23
  class ResourceGenerator < Rails::Generators::ResourceGenerator
9
- desc "Creates a DynamicImage resource"
24
+ include ActiveRecord::Generators::Migration
25
+
26
+ source_root File.expand_path("templates", __dir__)
10
27
 
11
28
  def initialize(args, *options)
12
- super(inject_dynamic_image_attributes(args), *options)
29
+ @orm_args = args
30
+ super
31
+ end
32
+
33
+ no_commands do
34
+ # Thor consumes the raw arguments while parsing, so {#initialize} keeps a copy for the ORM generator.
35
+ def invoke_orm_generator(orm)
36
+ invoke(orm, @orm_args, options.merge("migration" => false))
37
+ end
38
+ end
39
+
40
+ # The ORM generator would write its own migration, which can't carry nullability or indexes. Invoke it with
41
+ # +migration: false+ and render {#create_dynamic_image_migration} instead.
42
+ hook_for :orm, required: true, in: :rails, as: :model do |instance, orm|
43
+ instance.invoke_orm_generator(orm)
44
+ end
45
+
46
+ def create_dynamic_image_migration
47
+ migration_template(
48
+ "create_table_migration.rb",
49
+ File.join(db_migrate_path, "create_#{table_name}.rb")
50
+ )
13
51
  end
14
52
 
15
53
  def add_controller_extension
16
- inject_into_file(
54
+ inject_into_class(
17
55
  File.join("app/controllers",
18
56
  class_path,
19
57
  "#{file_name.pluralize}_controller.rb"),
20
- after: "ApplicationController\n"
58
+ "#{class_name.pluralize}Controller"
21
59
  ) do
22
60
  " include DynamicImage::Controller\n\n private\n\n " \
23
61
  "def model\n #{class_name}\n end\n"
@@ -25,9 +63,9 @@ module DynamicImage
25
63
  end
26
64
 
27
65
  def add_model_extension
28
- inject_into_file(
66
+ inject_into_class(
29
67
  File.join("app/models", class_path, "#{file_name}.rb"),
30
- after: "ActiveRecord::Base\n"
68
+ class_name
31
69
  ) do
32
70
  " include DynamicImage::Model\n"
33
71
  end
@@ -43,23 +81,21 @@ module DynamicImage
43
81
 
44
82
  private
45
83
 
46
- def inject_dynamic_image_attributes(args)
47
- if args.any?
48
- [args[0]] + dynamic_image_attributes + args[1..args.length]
49
- else
50
- args
51
- end
84
+ def schema_attributes
85
+ DynamicImage::Schema::ATTRIBUTES
86
+ end
87
+
88
+ def schema_indexes
89
+ DynamicImage::Schema::INDEXES
90
+ end
91
+
92
+ def index_columns(index)
93
+ columns = index[:columns]
94
+ columns.length == 1 ? columns.first.inspect : columns.inspect
52
95
  end
53
96
 
54
- def dynamic_image_attributes
55
- %w[content_hash:string content_type:string
56
- content_length:integer
57
- filename:string
58
- colorspace:string
59
- real_width:integer real_height:integer
60
- crop_width:integer crop_height:integer
61
- crop_start_x:integer crop_start_y:integer
62
- crop_gravity_x:integer crop_gravity_y:integer]
97
+ def attributes_with_index
98
+ attributes.select { |a| !a.reference? && a.has_index? }
63
99
  end
64
100
  end
65
101
  end
@@ -0,0 +1,32 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :<%= table_name %><%= primary_key_type %> do |t|
4
+ <% schema_attributes.each do |name, column| -%>
5
+ t.<%= column[:type] %> :<%= name %><%= ", null: false" unless column[:null] %>
6
+ <% end -%>
7
+ <% attributes.each do |attribute| -%>
8
+ <% if attribute.password_digest? -%>
9
+ t.string :password_digest<%= attribute.inject_options %>
10
+ <% elsif attribute.token? -%>
11
+ t.string :<%= attribute.name %><%= attribute.inject_options %>
12
+ <% elsif attribute.reference? -%>
13
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %>
14
+ <% elsif !attribute.virtual? -%>
15
+ t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %>
16
+ <% end -%>
17
+ <% end -%>
18
+
19
+ t.timestamps
20
+ end
21
+ <% schema_indexes.each do |index| -%>
22
+
23
+ add_index :<%= table_name %>, <%= index_columns(index) %><%= ", unique: true" if index[:unique] %>
24
+ <% end -%>
25
+ <% attributes.select(&:token?).each do |attribute| -%>
26
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
27
+ <% end -%>
28
+ <% attributes_with_index.each do |attribute| -%>
29
+ add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
30
+ <% end -%>
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ Description:
2
+ Brings an existing DynamicImage table up to date with the schema the
3
+ installed version of the gem expects. Compares the table against
4
+ DynamicImage::Schema and writes a migration containing only what is
5
+ missing. If nothing is missing, no migration is written.
6
+
7
+ Only additive changes are generated. Columns are added nullable, and
8
+ columns whose nullability differs from the expected schema are
9
+ reported rather than altered: change_column_null fails on existing
10
+ NULLs, and only you can know whether the data allows it.
11
+
12
+ Run it once per DynamicImage model.
13
+
14
+ Example:
15
+ bin/rails generate dynamic_image:upgrade Image
16
+
17
+ creates db/migrate/XXX_add_frame_count_and_alpha_to_images.rb
@@ -0,0 +1,10 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ <% missing_columns.each do |name, column| -%>
4
+ add_column :<%= table_name %>, :<%= name %>, :<%= column[:type] %>
5
+ <% end -%>
6
+ <% missing_indexes.each do |index| -%>
7
+ add_index :<%= table_name %>, <%= index_columns(index) %><%= ", unique: true" if index[:unique] %>
8
+ <% end -%>
9
+ end
10
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/named_base"
5
+ require "rails/generators/active_record/migration"
6
+
7
+ require "dynamic_image/schema"
8
+
9
+ module DynamicImage
10
+ module Generators
11
+ # Brings an existing table up to date with {DynamicImage::Schema}.
12
+ #
13
+ # bin/rails generate dynamic_image:upgrade Image
14
+ #
15
+ # The migration is derived from the difference between the table and the schema, so running the generator
16
+ # again once the migration has been applied writes nothing. An application several releases behind gets a single
17
+ # migration with everything it's missing.
18
+ #
19
+ # Only additive changes are generated. Columns are added nullable, and nullability differences are reported
20
+ # instead of altered: +change_column_null+ fails on existing NULLs, and the generator can't know whether the
21
+ # data allows it.
22
+ class UpgradeGenerator < Rails::Generators::NamedBase
23
+ include ActiveRecord::Generators::Migration
24
+
25
+ source_root File.expand_path("templates", __dir__)
26
+
27
+ def create_upgrade_migration
28
+ if missing_columns.empty? && missing_indexes.empty?
29
+ say_status(:identical, "#{table_name} is up to date", :blue)
30
+ return
31
+ end
32
+
33
+ migration_template("upgrade_migration.rb",
34
+ File.join(db_migrate_path, "#{migration_name}.rb"))
35
+ end
36
+
37
+ def report_nullability
38
+ nullable_in_table.each do |name|
39
+ say_status(:warn,
40
+ "#{table_name}.#{name} should be NOT NULL",
41
+ :yellow)
42
+ end
43
+ end
44
+
45
+ def report_index_locking
46
+ return if missing_indexes.empty?
47
+
48
+ say_status(:warn,
49
+ "adding an index locks the table while it builds",
50
+ :yellow)
51
+ end
52
+
53
+ private
54
+
55
+ def model_class
56
+ @model_class ||= class_name.constantize
57
+ end
58
+
59
+ def table_name
60
+ model_class.table_name
61
+ end
62
+
63
+ def existing_columns
64
+ @existing_columns ||= model_class.columns.index_by { |c| c.name.to_sym }
65
+ end
66
+
67
+ def existing_indexes
68
+ @existing_indexes ||=
69
+ model_class.connection.indexes(table_name).map do |index|
70
+ Array(index.columns).map(&:to_sym)
71
+ end
72
+ end
73
+
74
+ def missing_columns
75
+ @missing_columns ||=
76
+ DynamicImage::Schema::ATTRIBUTES.reject do |name, _|
77
+ existing_columns.key?(name)
78
+ end
79
+ end
80
+
81
+ # An index whose leading columns match already answers the query, so only a missing prefix counts as missing.
82
+ def missing_indexes
83
+ @missing_indexes ||=
84
+ DynamicImage::Schema::INDEXES.reject do |index|
85
+ columns = index[:columns]
86
+ existing_indexes.any? { |e| e.first(columns.length) == columns }
87
+ end
88
+ end
89
+
90
+ def nullable_in_table
91
+ DynamicImage::Schema::ATTRIBUTES.filter_map do |name, column|
92
+ existing = existing_columns[name]
93
+ name if existing && !column[:null] && existing.null
94
+ end
95
+ end
96
+
97
+ def index_columns(index)
98
+ columns = index[:columns]
99
+ columns.length == 1 ? columns.first.inspect : columns.inspect
100
+ end
101
+
102
+ def migration_name
103
+ if missing_columns.any?
104
+ "add_#{missing_columns.keys.join('_and_')}_to_#{table_name}"
105
+ else
106
+ "add_dynamic_image_indexes_to_#{table_name}"
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :dynamic_image do
4
+ desc "Fill in metadata columns for images stored before they existed"
5
+ task backfill: :environment do
6
+ unless ENV["MODELS"]
7
+ puts "Usage: #{$PROGRAM_NAME} dynamic_image:backfill MODELS=Image"
8
+ exit
9
+ end
10
+
11
+ ENV["MODELS"].split(",").map(&:strip).map(&:constantize).each do |model|
12
+ backfill = DynamicImage::Backfill.new(model)
13
+ total = backfill.pending.count
14
+ puts "#{model.name}: #{total} #{'record'.pluralize(total)} to read"
15
+
16
+ processed = 0
17
+ backfill.run do
18
+ processed += 1
19
+ print("\r #{processed}/#{total}") if (processed % 10).zero?
20
+ end
21
+
22
+ puts "\r #{backfill.updated} updated, #{backfill.skipped} skipped"
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamic_image
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.9
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -63,20 +63,14 @@ dependencies:
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: '2.2'
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- version: 2.2.1
66
+ version: '2.3'
70
67
  type: :runtime
71
68
  prerelease: false
72
69
  version_requirements: !ruby/object:Gem::Requirement
73
70
  requirements:
74
71
  - - "~>"
75
72
  - !ruby/object:Gem::Version
76
- version: '2.2'
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- version: 2.2.1
73
+ version: '2.3'
80
74
  description: DynamicImage is a Rails plugin that simplifies image uploading and processing
81
75
  email:
82
76
  - inge@elektronaut.no
@@ -91,36 +85,53 @@ files:
91
85
  - app/views/dynamic_image/images/show.html.erb
92
86
  - db/migrate/20190620160500_create_dynamic_image_variants.rb
93
87
  - lib/dynamic_image.rb
88
+ - lib/dynamic_image/backfill.rb
94
89
  - lib/dynamic_image/belongs_to.rb
90
+ - lib/dynamic_image/breakpoints.rb
95
91
  - lib/dynamic_image/controller.rb
96
92
  - lib/dynamic_image/digest_verifier.rb
97
93
  - lib/dynamic_image/engine.rb
98
94
  - lib/dynamic_image/errors.rb
99
95
  - lib/dynamic_image/format.rb
96
+ - lib/dynamic_image/format_negotiator.rb
100
97
  - lib/dynamic_image/helper.rb
98
+ - lib/dynamic_image/helper/formats.rb
99
+ - lib/dynamic_image/helper/pictures.rb
101
100
  - lib/dynamic_image/image_processor.rb
102
101
  - lib/dynamic_image/image_processor/colors.rb
103
102
  - lib/dynamic_image/image_processor/frames.rb
104
103
  - lib/dynamic_image/image_processor/transform.rb
105
104
  - lib/dynamic_image/image_reader.rb
106
105
  - lib/dynamic_image/image_sizing.rb
107
- - lib/dynamic_image/jobs.rb
108
- - lib/dynamic_image/jobs/create_variant.rb
109
106
  - lib/dynamic_image/metadata.rb
110
107
  - lib/dynamic_image/model.rb
111
108
  - lib/dynamic_image/model/dimensions.rb
112
109
  - lib/dynamic_image/model/transformations.rb
113
110
  - lib/dynamic_image/model/validations.rb
114
111
  - lib/dynamic_image/model/variants.rb
112
+ - lib/dynamic_image/picture.rb
113
+ - lib/dynamic_image/picture/format_policy.rb
115
114
  - lib/dynamic_image/processed_image.rb
115
+ - lib/dynamic_image/ratio.rb
116
116
  - lib/dynamic_image/routing.rb
117
+ - lib/dynamic_image/schema.rb
117
118
  - lib/dynamic_image/version.rb
119
+ - lib/rails/generators/dynamic_image/resource/USAGE
118
120
  - lib/rails/generators/dynamic_image/resource/resource_generator.rb
121
+ - lib/rails/generators/dynamic_image/resource/templates/create_table_migration.rb.tt
122
+ - lib/rails/generators/dynamic_image/upgrade/USAGE
123
+ - lib/rails/generators/dynamic_image/upgrade/templates/upgrade_migration.rb.tt
124
+ - lib/rails/generators/dynamic_image/upgrade/upgrade_generator.rb
125
+ - lib/tasks/dynamic_image.rake
119
126
  homepage: https://github.com/elektronaut/dynamic_image
120
127
  licenses:
121
128
  - MIT
122
129
  metadata:
130
+ bug_tracker_uri: https://github.com/elektronaut/dynamic_image/issues
131
+ changelog_uri: https://github.com/elektronaut/dynamic_image/blob/main/CHANGELOG.md
132
+ documentation_uri: https://www.rubydoc.info/gems/dynamic_image
123
133
  rubygems_mfa_required: 'true'
134
+ source_code_uri: https://github.com/elektronaut/dynamic_image
124
135
  rdoc_options: []
125
136
  require_paths:
126
137
  - lib
@@ -128,7 +139,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
128
139
  requirements:
129
140
  - - ">="
130
141
  - !ruby/object:Gem::Version
131
- version: 3.2.0
142
+ version: 3.3.0
132
143
  required_rubygems_version: !ruby/object:Gem::Requirement
133
144
  requirements:
134
145
  - - ">="
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DynamicImage
4
- module Jobs
5
- # = Create variant
6
- #
7
- # Creates an image variant.
8
- class CreateVariant < ActiveJob::Base
9
- queue_as :dis
10
-
11
- discard_on Dis::Errors::NotFoundError
12
-
13
- retry_on StandardError, attempts: 10, wait: :polynomially_longer
14
-
15
- def perform(record, options, size)
16
- size_v = Vector2d.parse(size)
17
- DynamicImage::ProcessedImage.new(record, options)
18
- .find_or_create_variant(size_v)
19
- end
20
- end
21
- end
22
- end
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dynamic_image/jobs/create_variant"