cloudhdr_rails 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.
Files changed (34) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +29 -0
  4. data/app/assets/javascripts/cloudhdr/updater.js +115 -0
  5. data/app/assets/stylesheets/cloudhdr/updater.css +8 -0
  6. data/app/controllers/cloudhdr_controller.rb +64 -0
  7. data/app/helpers/cloudhdr_helper.rb +245 -0
  8. data/app/models/cloudhdr_job.rb +189 -0
  9. data/app/views/cloudhdr/_thumbnail_update.html.erb +3 -0
  10. data/app/views/cloudhdr/multi_thumbnail_update.json.erb +7 -0
  11. data/app/views/cloudhdr/thumbnail_update.js.erb +1 -0
  12. data/config/routes.rb +8 -0
  13. data/lib/cloudhdr_rails/acts_as_cloudhdr.rb +1020 -0
  14. data/lib/cloudhdr_rails/engine.rb +4 -0
  15. data/lib/cloudhdr_rails/errors.rb +46 -0
  16. data/lib/cloudhdr_rails/version.rb +3 -0
  17. data/lib/cloudhdr_rails.rb +7 -0
  18. data/lib/generators/cloudhdr_rails/model_update_generator.rb +52 -0
  19. data/lib/generators/cloudhdr_rails/scaffold_generator.rb +94 -0
  20. data/lib/generators/cloudhdr_rails/setup_generator.rb +33 -0
  21. data/lib/generators/cloudhdr_rails/templates/cloudhdr.yml +19 -0
  22. data/lib/generators/cloudhdr_rails/templates/controller.rb +71 -0
  23. data/lib/generators/cloudhdr_rails/templates/helper.rb +5 -0
  24. data/lib/generators/cloudhdr_rails/templates/migration.rb +21 -0
  25. data/lib/generators/cloudhdr_rails/templates/model.rb +15 -0
  26. data/lib/generators/cloudhdr_rails/templates/model_migration.rb +56 -0
  27. data/lib/generators/cloudhdr_rails/templates/model_thumbnail.rb +5 -0
  28. data/lib/generators/cloudhdr_rails/templates/model_update_migration.rb +64 -0
  29. data/lib/generators/cloudhdr_rails/templates/views/_form.html.erb.tt +23 -0
  30. data/lib/generators/cloudhdr_rails/templates/views/index.html.erb.tt +26 -0
  31. data/lib/generators/cloudhdr_rails/templates/views/new.html.erb.tt +5 -0
  32. data/lib/generators/cloudhdr_rails/templates/views/show.html.erb.tt +12 -0
  33. data/lib/tasks/cloudhdr_rails_tasks.rake +19 -0
  34. metadata +212 -0
@@ -0,0 +1,4 @@
1
+ module CloudhdrRails
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ module ActsAsCloudhdr
2
+
3
+ class Error < StandardError
4
+
5
+ def initialize(error_or_message)
6
+ if error_or_message.is_a?(Exception)
7
+ @error = error_or_message
8
+ else
9
+ @message = error_or_message
10
+ end
11
+ end
12
+
13
+ def message
14
+ @message || "#{@error.class} (wrapped in a #{self.class}) - #{@error.message}"
15
+ end
16
+
17
+ def backtrace
18
+ if @error
19
+ @error.backtrace
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ def inspect
26
+ if @error
27
+ "#{@error.inspect} (wrapped in a #{self.class})"
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def to_s
34
+ if @error
35
+ "#{@error.class} (wrapped in a #{self.class}) - #{@error}"
36
+ else
37
+ super
38
+ end
39
+ end
40
+ end
41
+
42
+ class HTTPError < Error; end
43
+ class ThumbnailNotFoundError < Error; end
44
+ class ThumbnailAttributesNotFoundError < Error; end
45
+
46
+ end
@@ -0,0 +1,3 @@
1
+ module CloudhdrRails
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "cloudhdr_rails/engine"
2
+
3
+ require 'cloudhdr_rails/acts_as_cloudhdr'
4
+ require 'cloudhdr_rails/errors'
5
+
6
+ module CloudhdrRails
7
+ end
@@ -0,0 +1,52 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ module CloudhdrRails
4
+ module Generators
5
+
6
+ class ModelUpdateGenerator < Rails::Generators::NamedBase
7
+
8
+ include Rails::Generators::Migration
9
+
10
+ #argument :model_names, :type => :array, :default => [], :banner => "action action"
11
+ #check_class_collision :suffix => "CloudhdrRails"
12
+
13
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
14
+
15
+ def self.next_migration_number(dirname)
16
+ if ActiveRecord::Base.timestamped_migrations
17
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
18
+ else
19
+ "%.3d" % (current_migration_number(dirname) + 1)
20
+ end
21
+ end
22
+
23
+ def create_migration_file
24
+ migration_template 'model_update_migration.rb', "db/migrate/make_#{file_name.pluralize}_cloudhdr.rb"
25
+ #migration_template 'model_thumbnail_migration.rb', "db/migrate/create_#{file_name.singularize}_thumbnails.rb"
26
+ end
27
+
28
+ def create_model_file
29
+ #template 'model.rb', File.join('app/models', class_path, "#{file_name.singularize}.rb")
30
+ template 'model_thumbnail.rb', File.join('app/models', class_path, "#{file_name.singularize}_thumbnail.rb")
31
+ end
32
+
33
+
34
+ protected
35
+
36
+ #def create_views_for(engine)
37
+ # for state in model_names do
38
+ # @state = state
39
+ # @path = File.join('app/cells', file_name, "#{state}.html.#{engine}")
40
+ #
41
+ # template "view.#{engine}", @path
42
+ # end
43
+ #end
44
+
45
+
46
+
47
+
48
+ end
49
+
50
+ end
51
+ end
52
+
@@ -0,0 +1,94 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ module CloudhdrRails
4
+ module Generators
5
+
6
+ class ScaffoldGenerator < Rails::Generators::NamedBase
7
+
8
+ include Rails::Generators::Migration
9
+
10
+ #argument :model_names, :type => :array, :default => [], :banner => "action action"
11
+ #check_class_collision :suffix => "CloudhdrRails"
12
+
13
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
14
+
15
+ def self.next_migration_number(dirname)
16
+ if ActiveRecord::Base.timestamped_migrations
17
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
18
+ else
19
+ "%.3d" % (current_migration_number(dirname) + 1)
20
+ end
21
+ end
22
+
23
+ def create_migration_file
24
+ migration_template 'model_migration.rb', "db/migrate/create_#{file_name.pluralize}.rb"
25
+ #migration_template 'model_thumbnail_migration.rb', "db/migrate/create_#{file_name.singularize}_thumbnails.rb"
26
+ end
27
+
28
+ def create_model_file
29
+ template 'model.rb', File.join('app/models', class_path, "#{file_name.singularize}.rb")
30
+ template 'model_thumbnail.rb', File.join('app/models', class_path, "#{file_name.singularize}_thumbnail.rb")
31
+ end
32
+
33
+ def create_helper_file
34
+ template 'helper.rb', File.join('app/helpers', class_path, "#{file_name.pluralize}_helper.rb")
35
+ end
36
+
37
+ def create_controller_file
38
+ template 'controller.rb', File.join('app/controllers', class_path, "#{file_name.pluralize}_controller.rb")
39
+ end
40
+
41
+ def create_views
42
+ directory 'views', File.join('app/views', class_path, "#{file_name.pluralize}" )
43
+ #["_form.html.erb","index.html.erb","new.html.erb","show.html.erb"].each do |view|
44
+ # template "views/#{view}", File.join('app/views', class_path, "#{file_name.pluralize}", view )
45
+ #end
46
+ end
47
+
48
+ def create_route
49
+ route("resources :#{file_name.pluralize}, :except=>[:edit,:update]")
50
+ end
51
+
52
+ #class_option :view_engine, :type => :string, :aliases => "-t", :desc => "Template engine for the views. Available options are 'erb' and 'haml'.", :default => "erb"
53
+ #class_option :haml, :type => :boolean, :default => false
54
+
55
+ def do_a_thing
56
+ #puts "We are doing a thing!!!!!!!!!!!!!!!! #{file_name}"
57
+ end
58
+
59
+ #def create_cell_file
60
+ # template 'cell.rb', File.join('app/cells', class_path, "#{file_name}_cell.rb")
61
+ #end
62
+
63
+ #def create_views
64
+ # if options[:view_engine].to_s == "haml" or options[:haml]
65
+ # create_views_for(:haml)
66
+ # else
67
+ # create_views_for(:erb)
68
+ # end
69
+ #end
70
+
71
+ #def create_test
72
+ # @states = model_names
73
+ # template 'cell_test.rb', File.join('test/cells/', "#{file_name}_cell_test.rb")
74
+ #end
75
+
76
+ protected
77
+
78
+ #def create_views_for(engine)
79
+ # for state in model_names do
80
+ # @state = state
81
+ # @path = File.join('app/cells', file_name, "#{state}.html.#{engine}")
82
+ #
83
+ # template "view.#{engine}", @path
84
+ # end
85
+ #end
86
+
87
+
88
+
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+
@@ -0,0 +1,33 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ module CloudhdrRails
4
+ module Generators
5
+
6
+ class SetupGenerator < Rails::Generators::Base
7
+
8
+ include Rails::Generators::Migration
9
+ def self.source_root
10
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
11
+ end
12
+
13
+ def self.next_migration_number(dirname)
14
+ if ActiveRecord::Base.timestamped_migrations
15
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
16
+ else
17
+ "%.3d" % (current_migration_number(dirname) + 1)
18
+ end
19
+ end
20
+
21
+ def create_migration_file
22
+ migration_template 'migration.rb', 'db/migrate/create_cloudhdr_jobs.rb'
23
+ end
24
+
25
+ def create_config_file
26
+ template 'cloudhdr.yml', File.join('config','cloudhdr.yml')
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+
@@ -0,0 +1,19 @@
1
+ development:
2
+ cloudhdr_api_key: asdf
3
+ base_url: http://your.host.test.com
4
+ storeage_mode : s3
5
+ s3_bucket_name: bucket.name
6
+
7
+ test:
8
+ cloudhdr_api_key: asdf
9
+ cloudhdr_url: http://some-test-domian.com
10
+ base_url: http://your.host.test.com
11
+ storeage_mode : local
12
+ s3_bucket_name: bucket.name
13
+
14
+ production:
15
+ cloudhdr_api_key: asdf
16
+ base_url: http://your.host.test.com
17
+ storeage_mode : s3
18
+ processing_mode : resque
19
+ s3_bucket_name: bucket.name
@@ -0,0 +1,71 @@
1
+ class <%= name.classify.pluralize %>Controller < ApplicationController
2
+
3
+ # This example does not handle all of the ins and outs of
4
+ # allowing someone to 'replace' a file by edit/update.
5
+ # That's a sticky problem that would only cloud
6
+ # the concepts we're demonstrating here.
7
+
8
+
9
+ # GET /<%= name.pluralize %>
10
+ # GET /<%= name.pluralize %>.xml
11
+ def index
12
+ @<%= name.pluralize %> = <%= name.classify %>.top_level.all
13
+
14
+ respond_to do |format|
15
+ format.html # index.html.erb
16
+ format.xml { render :xml => @<%= name.pluralize %> }
17
+ end
18
+ end
19
+
20
+ # GET /<%= name.pluralize %>/1
21
+ # GET /<%= name.pluralize %>/1.xml
22
+ def show
23
+ @<%= name.singularize %> = <%= name.classify %>.find(params[:id])
24
+
25
+ respond_to do |format|
26
+ format.html # show.html.erb
27
+ format.xml { render :xml => @<%= name.singularize %> }
28
+ end
29
+ end
30
+
31
+ # GET /<%= name.pluralize %>/new
32
+ # GET /<%= name.pluralize %>/new.xml
33
+ def new
34
+ @<%= name.singularize %> = <%= name.classify %>.new
35
+
36
+ respond_to do |format|
37
+ format.html # new.html.erb
38
+ format.xml { render :xml => @<%= name.singularize %> }
39
+ end
40
+ end
41
+
42
+
43
+ # POST /<%= name.pluralize %>
44
+ # POST /<%= name.pluralize %>.xml
45
+ def create
46
+ @<%= name.singularize %> = <%= name.classify %>.new(params[:<%= name.singularize %>])
47
+
48
+ respond_to do |format|
49
+ if @<%= name.singularize %>.save
50
+ format.html { redirect_to(@<%= name.singularize %>, :notice => "<%= name.classify %> was successfully created.") }
51
+ format.xml { render :xml => @<%= name.singularize %>, :status => :created, :location => @<%= name.singularize %> }
52
+ else
53
+ format.html { render :action => "new" }
54
+ format.xml { render :xml => @<%= name.singularize %>.errors, :status => :unprocessable_entity }
55
+ end
56
+ end
57
+ end
58
+
59
+
60
+ # DELETE /<%= name.pluralize %>/1
61
+ # DELETE /<%= name.pluralize %>/1.xml
62
+ def destroy
63
+ @<%= name.singularize %> = <%= name.classify %>.find(params[:id])
64
+ @<%= name.singularize %>.destroy
65
+
66
+ respond_to do |format|
67
+ format.html { redirect_to(<%= name.pluralize %>_url) }
68
+ format.xml { head :ok }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ module <%= name.classify.pluralize %>Helper
2
+
3
+
4
+
5
+ end
@@ -0,0 +1,21 @@
1
+ class CreateCloudhdrJobs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :cloudhdr_jobs do |t|
4
+ t.string "image_type"
5
+ t.integer "image_id"
6
+ t.integer "cloudhdr_job_id"
7
+ t.integer "cloudhdr_input_id"
8
+ t.integer "cloudhdr_output_id"
9
+ t.string "cloudhdr_status"
10
+ t.string "tracking_mode"
11
+ t.integer "user_id"
12
+ t.timestamps
13
+ end
14
+ add_index :cloudhdr_jobs, [:image_type, :image_id]
15
+ add_index :cloudhdr_jobs, :id
16
+ end
17
+
18
+ def self.down
19
+ drop_table :cloudhdr_jobs
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ class <%= name.classify %> < ActiveRecord::Base
2
+
3
+ acts_as_cloudhdr :thumbnail_class => "<%= name.classify %>Thumbnail",
4
+ :thumbnails => [
5
+ {:label=>"small",:width=>100,:height=>100 },
6
+ {:label=>"medium",:width=>400,:height=>400,
7
+ :frame=>{ :width=>20, :bottom=>50, :color=>'003' },
8
+ :annotations=>[
9
+ {:text=>"Annotation Testing",:pointsize=>30,:fill_color=>'fff',:gravity=>"South",:y=>10},
10
+ {:text=>"Howdy!",:pointsize=>10,:fill_color=>'ccc',:gravity=>"North",:y=>5}
11
+ ]
12
+ }
13
+ ]
14
+
15
+ end
@@ -0,0 +1,56 @@
1
+ class Create<%= name.classify.pluralize %> < ActiveRecord::Migration
2
+ def self.up
3
+
4
+ create_table :<%= name.pluralize %> do |t|
5
+ t.string "filename"
6
+ t.string "content_type"
7
+ t.integer "duration_in_ms"
8
+ t.integer "width"
9
+ t.integer "height"
10
+ t.integer "file_size"
11
+ t.string "upload_host"
12
+ t.datetime "created_at"
13
+ t.datetime "updated_at"
14
+ t.datetime "taken_at"
15
+ t.float "lat"
16
+ t.float "lng"
17
+ t.string "cloudhdr_status"
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :<%= name.pluralize %>, :id
23
+
24
+
25
+ create_table :<%= name.singularize %>_thumbnails do |t|
26
+ t.string "filename"
27
+ t.string "content_type"
28
+ t.integer "duration_in_ms"
29
+ t.integer "width"
30
+ t.integer "height"
31
+ t.integer "file_size"
32
+ t.string "upload_host"
33
+ t.datetime "created_at"
34
+ t.datetime "updated_at"
35
+ t.datetime "taken_at"
36
+ t.float "lat"
37
+ t.float "lng"
38
+ t.string "cloudhdr_status"
39
+
40
+ t.string "thumbnail"
41
+ t.integer "parent_id"
42
+ t.string "parent_type"
43
+
44
+ t.timestamps
45
+ end
46
+
47
+ add_index :<%= name.singularize %>_thumbnails, :id
48
+ add_index :<%= name.singularize %>_thumbnails, :parent_id
49
+
50
+ end
51
+
52
+ def self.down
53
+ drop_table :<%= name.pluralize %>
54
+ drop_table :<%= name.singularize %>_thumbnails
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ class <%= name.classify %>Thumbnail < ActiveRecord::Base
2
+
3
+ acts_as_cloudhdr :parent_class => "<%= name.classify %>"
4
+
5
+ end
@@ -0,0 +1,64 @@
1
+ class Make<%= name.classify.pluralize %>Cloudhdr < ActiveRecord::Migration
2
+ def self.up
3
+
4
+
5
+ add_column :<%= name.pluralize %>, :filename, :string
6
+ add_column :<%= name.pluralize %>, :content_type, :string
7
+ add_column :<%= name.pluralize %>, :duration_in_ms, :integer
8
+ add_column :<%= name.pluralize %>, :width, :integer
9
+ add_column :<%= name.pluralize %>, :height, :integer
10
+ add_column :<%= name.pluralize %>, :file_size, :integer
11
+ add_column :<%= name.pluralize %>, :upload_host, :string
12
+ add_column :<%= name.pluralize %>, :taken_at, :datetime
13
+ add_column :<%= name.pluralize %>, :lat, :float
14
+ add_column :<%= name.pluralize %>, :lng, :float
15
+ add_column :<%= name.pluralize %>, :cloudhdr_status, :string
16
+
17
+
18
+
19
+ create_table :<%= name.singularize %>_thumbnails do |t|
20
+ t.string "filename"
21
+ t.string "content_type"
22
+ t.integer "duration_in_ms"
23
+ t.integer "width"
24
+ t.integer "height"
25
+ t.integer "file_size"
26
+ t.string "upload_host"
27
+ t.datetime "created_at"
28
+ t.datetime "updated_at"
29
+ t.datetime "taken_at"
30
+ t.float "lat"
31
+ t.float "lng"
32
+ t.string "cloudhdr_status"
33
+
34
+ t.string "thumbnail"
35
+ t.integer "parent_id"
36
+ t.string "parent_type"
37
+
38
+ t.timestamps
39
+ end
40
+
41
+ add_index :<%= name.singularize %>_thumbnails, :id
42
+ add_index :<%= name.singularize %>_thumbnails, :parent_id
43
+
44
+ end
45
+
46
+ def self.down
47
+
48
+ remove_column :<%= name.pluralize %>, :filename
49
+ remove_column :<%= name.pluralize %>, :content_type
50
+ remove_column :<%= name.pluralize %>, :duration_in_ms
51
+ remove_column :<%= name.pluralize %>, :width
52
+ remove_column :<%= name.pluralize %>, :height
53
+ remove_column :<%= name.pluralize %>, :file_size
54
+ remove_column :<%= name.pluralize %>, :upload_host
55
+ remove_column :<%= name.pluralize %>, :created_at
56
+ remove_column :<%= name.pluralize %>, :updated_at
57
+ remove_column :<%= name.pluralize %>, :taken_at
58
+ remove_column :<%= name.pluralize %>, :lat
59
+ remove_column :<%= name.pluralize %>, :lng
60
+ remove_column :<%= name.pluralize %>, :cloudhdr_status
61
+
62
+ drop_table :<%= name.singularize %>_thumbnails
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ <%%= form_for( @<%= file_name.singularize %>, :html => { :multipart => true } ) do |f| %>
2
+
3
+ <%% if @<%= file_name.singularize %>.errors.any? %>
4
+ <div id="error_explanation">
5
+ <h2><%%= pluralize(@<%= file_name.singularize %>.errors.count, "error") %> prohibited this image from being saved:</h2>
6
+ <ul>
7
+ <%% @<%= file_name.singularize %>.errors.full_messages.each do |msg| %>
8
+ <li><%%= msg %></li>
9
+ <%% end %>
10
+ </ul>
11
+ </div>
12
+ <%% end %>
13
+
14
+ <div class="field">
15
+ <%%= f.label :file, "Upload image file" %>
16
+ <%%= f.file_field :file %>
17
+ </div>
18
+
19
+ <div class="actions">
20
+ <%%= f.submit %>
21
+ </div>
22
+
23
+ <%% end %>
@@ -0,0 +1,26 @@
1
+ <h1>Listing <%= file_name %></h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <th>Filename</th>
6
+ <th></th>
7
+ <th></th>
8
+
9
+ </tr>
10
+
11
+ <%% @<%= file_name.pluralize %>.each do |<%= file_name.singularize %>| %>
12
+ <tr>
13
+ <td>
14
+ <%%= cloudhdr_thumbnail <%= file_name.singularize %>,"small",false %><br/>
15
+ <%%= <%= file_name.singularize %>.filename %>
16
+ </td>
17
+ <td><%%= link_to 'Show', <%= file_name.singularize %> %></td>
18
+
19
+ <td><%%= link_to 'Destroy', <%= file_name.singularize %>, :confirm => 'Are you sure?', :method => :delete %></td>
20
+ </tr>
21
+ <%% end %>
22
+ </table>
23
+
24
+ <br />
25
+
26
+ <%%= link_to 'New <%= file_name.singularize %>', new_<%= file_name.singularize %>_path %>
@@ -0,0 +1,5 @@
1
+ <h1>New <%= file_name.singularize %></h1>
2
+
3
+ <%%= render 'form' %>
4
+
5
+ <%%= link_to 'Back', <%= file_name.pluralize %>_path %>
@@ -0,0 +1,12 @@
1
+ <p id="notice"><%%= notice %></p>
2
+
3
+ <p>
4
+ <b>Filename:</b>
5
+ <%%= @<%= file_name.singularize %>.filename %>
6
+ </p>
7
+
8
+ <%%= cloudhdr_thumbnail @<%= file_name.singularize %>,"medium" %>
9
+
10
+ <br/>
11
+
12
+ <%%= link_to 'Back', <%= file_name.pluralize %>_path %>
@@ -0,0 +1,19 @@
1
+ # desc "Explaining what the task does"
2
+ # task :cloudhdr_rails do
3
+ # # Task goes here
4
+ # end
5
+
6
+ namespace :cloudhdr_rails do
7
+ desc "Update pending CloudhdrJobs"
8
+ task :update_pending_jobs => :environment do
9
+ #e = CloudhdrJob.first
10
+ #e.encodable # try to load cloudhdr config
11
+ #e.encodable.class.read_cloudhdr_configuration
12
+ ActsAsCloudhdr.read_cloudhdr_configuration
13
+ #puts "api key = #{cloudhdr.api_key}"
14
+ #puts "Rails.env = #{Rails.env}"
15
+ puts "Updating #{CloudhdrJob.pending.count} CloudhdrJobs"
16
+ CloudhdrJob.update_pending_jobs
17
+ end
18
+ end
19
+