fogged 0.0.2
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/MIT-LICENSE +20 -0
- data/Rakefile +34 -0
- data/app/controllers/fogged/resources_controller.rb +259 -0
- data/app/jobs/fogged/resources/zencoder_poll_job.rb +39 -0
- data/app/models/fogged/resource.rb +153 -0
- data/app/models/fogged/resources/aws_encoder.rb +64 -0
- data/app/models/fogged/resources/encoder.rb +13 -0
- data/app/serializers/fogged/resource_serializer.rb +35 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/fr.yml +6 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20141103143408_create_fogged_resources.rb +20 -0
- data/lib/fogged.rb +46 -0
- data/lib/fogged/acts_as_having_many_resources.rb +30 -0
- data/lib/fogged/acts_as_having_one_resource.rb +34 -0
- data/lib/fogged/engine.rb +50 -0
- data/lib/fogged/version.rb +3 -0
- data/lib/tasks/fogged_tasks.rake +4 -0
- data/test/controllers/fogged/resources_controller/confirm_test.rb +56 -0
- data/test/controllers/fogged/resources_controller/create_test.rb +56 -0
- data/test/controllers/fogged/resources_controller/destroy_test.rb +29 -0
- data/test/controllers/fogged/resources_controller/show_test.rb +35 -0
- data/test/controllers/fogged/resources_controller/update_test.rb +31 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/images_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/image.rb +3 -0
- data/test/dummy/app/models/movie.rb +4 -0
- data/test/dummy/app/models/movie_fogged_resource.rb +4 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/delayed_job +5 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +78 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/assets.rb +8 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/fog.rb +1 -0
- data/test/dummy/config/initializers/fogged.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20141104135257_create_images.rb +10 -0
- data/test/dummy/db/migrate/20141104152227_create_movies.rb +9 -0
- data/test/dummy/db/migrate/20141104152345_create_movie_fogged_resources.rb +10 -0
- data/test/dummy/db/migrate/20141105073909_create_delayed_jobs.rb +22 -0
- data/test/dummy/db/schema.rb +72 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +234 -0
- data/test/dummy/log/test.log +20250 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/test/controllers/resources_controller/index_test.rb +56 -0
- data/test/dummy/test/models/image_test.rb +24 -0
- data/test/dummy/test/models/movie_test.rb +26 -0
- data/test/fixtures/fogged/resources.yml +31 -0
- data/test/fixtures/images.yml +5 -0
- data/test/fixtures/movie_fogged_resources.yml +19 -0
- data/test/fixtures/movies.yml +8 -0
- data/test/fogged_test.rb +7 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/jobs/fogged/resources/zencoder_poll_job_test.rb +92 -0
- data/test/models/fogged/resource_test.rb +97 -0
- data/test/models/fogged/resources/aws_encoder_test.rb +54 -0
- data/test/models/fogged/resources/encoder_test.rb +18 -0
- data/test/support/concerns/json_test_helper.rb +43 -0
- data/test/support/concerns/resource_test_helper.rb +28 -0
- data/test/test_helper.rb +59 -0
- metadata +354 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
module Resources
|
|
3
|
+
class AWSEncoder < Struct.new(:resource)
|
|
4
|
+
def encode!
|
|
5
|
+
encode_video if resource.video?
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def encode_video
|
|
11
|
+
fail(ArgumentError, "Zencoder gem needed") unless defined?(Zencoder)
|
|
12
|
+
fail(ArgumentError, "Delayed Job gem needed") unless defined?(Delayed::Job)
|
|
13
|
+
|
|
14
|
+
job = Zencoder::Job.create(
|
|
15
|
+
:input => resource.url,
|
|
16
|
+
:region => "europe",
|
|
17
|
+
:download_connections => 5,
|
|
18
|
+
:output => output
|
|
19
|
+
)
|
|
20
|
+
resource.update!(
|
|
21
|
+
:encoding_job_id => job.body["id"].to_s,
|
|
22
|
+
:encoding_progress => 0
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
Delayed::Job.enqueue(ZencoderPollJob.new(resource.id))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def output
|
|
29
|
+
[
|
|
30
|
+
{
|
|
31
|
+
:url => "s3://#{bucket}/#{fogged_name_for(:h264)}",
|
|
32
|
+
:video_codec => "h264",
|
|
33
|
+
:public => 1,
|
|
34
|
+
:thumbnails => {
|
|
35
|
+
:number => 5,
|
|
36
|
+
:format => "png",
|
|
37
|
+
:base_url => "s3://#{bucket}",
|
|
38
|
+
:filename => "#{resource.token}-thumbnail-{{number}}",
|
|
39
|
+
:public => 1
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
:url => "s3://#{bucket}/#{fogged_name_for(:mpeg)}",
|
|
44
|
+
:video_codec => "mpeg4",
|
|
45
|
+
:public => 1
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
:url => "s3://#{bucket}/#{fogged_name_for(:webm)}",
|
|
49
|
+
:video_codec => "vp8",
|
|
50
|
+
:public => 1
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def bucket
|
|
56
|
+
resource.send(:fogged_file).directory.key
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def fogged_name_for(type)
|
|
60
|
+
resource.send(:fogged_name_for, type)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
module Resources
|
|
3
|
+
class Encoder
|
|
4
|
+
def self.for(resource)
|
|
5
|
+
"Fogged::Resources::#{provider_for(resource)}Encoder".constantize.new(resource)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.provider_for(resource)
|
|
9
|
+
return :AWS if resource.send(:fogged_file).class.to_s.include?("AWS")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
class ResourceSerializer < ActiveModel::Serializer
|
|
3
|
+
attributes :id, :name, :upload_url, :url
|
|
4
|
+
attributes :h264_url, :mpeg_url, :webm_url, :thumbnail_urls
|
|
5
|
+
attributes :encoding_progress
|
|
6
|
+
|
|
7
|
+
def include_upload_url?
|
|
8
|
+
options[:include_upload_url]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def include_h264_url?
|
|
12
|
+
object.video?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def include_mpeg_url?
|
|
16
|
+
object.video?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def include_webm_url?
|
|
20
|
+
object.video?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def include_thumbnail_urls?
|
|
24
|
+
object.video?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def include_encoding_progress?
|
|
28
|
+
object.video?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def encoding_progress
|
|
32
|
+
object.encoding_progress || 0
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateFoggedResources < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :fogged_resources do |t|
|
|
4
|
+
t.string :name, :null => false
|
|
5
|
+
t.string :token, :null => false
|
|
6
|
+
t.integer :width
|
|
7
|
+
t.integer :height
|
|
8
|
+
t.string :extension, :null => false
|
|
9
|
+
t.boolean :uploading
|
|
10
|
+
t.string :content_type, :null => false
|
|
11
|
+
t.integer :encoding_progress
|
|
12
|
+
t.string :encoding_job_id
|
|
13
|
+
t.integer :duration
|
|
14
|
+
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
add_index :fogged_resources, :token
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/fogged.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Gem.loaded_specs["fogged"].dependencies.select { |d| d.type == :runtime }.each do |d|
|
|
2
|
+
require d.name
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
require "fogged/engine"
|
|
6
|
+
require "fogged/acts_as_having_one_resource"
|
|
7
|
+
require "fogged/acts_as_having_many_resources"
|
|
8
|
+
|
|
9
|
+
module Fogged
|
|
10
|
+
mattr_accessor :provider
|
|
11
|
+
@@provider = nil
|
|
12
|
+
|
|
13
|
+
mattr_accessor :resources
|
|
14
|
+
@@resources = nil
|
|
15
|
+
|
|
16
|
+
mattr_accessor :test_enabled
|
|
17
|
+
@@test_enabled = false
|
|
18
|
+
|
|
19
|
+
# controller
|
|
20
|
+
mattr_accessor :parent_controller
|
|
21
|
+
@@parent_controller = "ApplicationController"
|
|
22
|
+
|
|
23
|
+
# aws
|
|
24
|
+
mattr_accessor :aws_key
|
|
25
|
+
@@aws_key = nil
|
|
26
|
+
mattr_accessor :aws_secret
|
|
27
|
+
@@aws_secret = nil
|
|
28
|
+
mattr_accessor :aws_bucket
|
|
29
|
+
@@aws_bucket = nil
|
|
30
|
+
mattr_accessor :aws_region
|
|
31
|
+
@@aws_region = nil
|
|
32
|
+
|
|
33
|
+
# zencoder
|
|
34
|
+
mattr_accessor :zencoder_enabled
|
|
35
|
+
@@zencoder_enabled = false
|
|
36
|
+
mattr_accessor :zencoder_polling_frequency
|
|
37
|
+
@@zencoder_polling_frequency = 10
|
|
38
|
+
|
|
39
|
+
def self.configure
|
|
40
|
+
yield self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.test_mode!
|
|
44
|
+
self.test_enabled = true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
module ActsAsHavingManyResources
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
module ClassMethods
|
|
6
|
+
DEFAULT_OPTIONS = {
|
|
7
|
+
:dependent => :destroy,
|
|
8
|
+
:class_name => "Fogged::Resource"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def acts_as_having_many_resources(*args)
|
|
12
|
+
options = args.extract_options!
|
|
13
|
+
unless options.include?(:through)
|
|
14
|
+
fail(ArgumentError, ":through option is mandatory")
|
|
15
|
+
end
|
|
16
|
+
has_many :resources, DEFAULT_OPTIONS.merge(options)
|
|
17
|
+
validate :_check_resources, :unless => "resources.empty?"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def _check_resources
|
|
24
|
+
return if resources.to_a.select(&:uploading).empty?
|
|
25
|
+
errors.add(:resources, I18n.t("fogged.resources.still_uploading"))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
ActiveRecord::Base.send(:include, Fogged::ActsAsHavingManyResources)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
module ActsAsHavingOneResource
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
module ClassMethods
|
|
6
|
+
DEFAULT_OPTIONS = {
|
|
7
|
+
:dependent => :destroy,
|
|
8
|
+
:class_name => "Fogged::Resource"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def acts_as_having_one_resource(*args)
|
|
12
|
+
belongs_to :resource, DEFAULT_OPTIONS.merge(args.extract_options!)
|
|
13
|
+
validate :_check_resource, :unless => "resource.blank?"
|
|
14
|
+
|
|
15
|
+
define_method(:resource_id) do
|
|
16
|
+
resource.try(:id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
define_method(:resource_id=) do |id|
|
|
20
|
+
self.resource = id.blank? ? nil : Resource.find(id)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def _check_resource
|
|
28
|
+
return unless resource.uploading
|
|
29
|
+
errors.add(:resource, I18n.t("fogged.resource.still_uploading"))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ActiveRecord::Base.send(:include, Fogged::ActsAsHavingOneResource)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Fogged
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace Fogged
|
|
4
|
+
config.fogged = Fogged
|
|
5
|
+
|
|
6
|
+
initializer "fogged.detect_zencoder" do
|
|
7
|
+
if defined?(Zencoder) && defined?(Delayed::Job)
|
|
8
|
+
Fogged.zencoder_enabled = true
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer "fogged.resources" do
|
|
13
|
+
unless Fogged.test_enabled
|
|
14
|
+
case Fogged.provider
|
|
15
|
+
when :aws
|
|
16
|
+
fail(ArgumentError, "AWS key is mandatory") unless Fogged.aws_key
|
|
17
|
+
fail(ArgumentError, "AWS secret is mandatory") unless Fogged.aws_secret
|
|
18
|
+
fail(ArgumentError, "AWS bucket is mandatory") unless Fogged.aws_bucket
|
|
19
|
+
storage_options = {
|
|
20
|
+
:provider => "AWS",
|
|
21
|
+
:aws_access_key_id => Fogged.aws_key,
|
|
22
|
+
:aws_secret_access_key => Fogged.aws_secret
|
|
23
|
+
}
|
|
24
|
+
storage_options.merge!(:region => Fogged.aws_region) if Fogged.aws_region
|
|
25
|
+
storage = Fog::Storage.new(storage_options)
|
|
26
|
+
|
|
27
|
+
Fogged.resources = storage.directories.get(Fogged.aws_bucket)
|
|
28
|
+
if Rails.env.test?
|
|
29
|
+
Fog.mock!
|
|
30
|
+
Fogged.resources = storage.directories.create(:key => Fogged.aws_bucket)
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
fail(ArgumentError, "Provider #{Fogged.config.provider} is not available!")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
initializer "fogged.resources.test" do
|
|
39
|
+
if Fogged.test_enabled
|
|
40
|
+
Fog.mock!
|
|
41
|
+
storage = Fog::Storage.new(
|
|
42
|
+
:provider => "AWS",
|
|
43
|
+
:aws_access_key_id => "1234567890",
|
|
44
|
+
:aws_secret_access_key => "1234567890"
|
|
45
|
+
)
|
|
46
|
+
Fogged.resources = storage.directories.create(:key => "test")
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Fogged
|
|
4
|
+
class ResourcesControllerConfirmTest < ActionController::TestCase
|
|
5
|
+
tests ResourcesController
|
|
6
|
+
include ResourceTestHelper
|
|
7
|
+
|
|
8
|
+
test "should confirm resource" do
|
|
9
|
+
resource = fogged_resources(:resource_png)
|
|
10
|
+
FastImage.expects(:size).once.returns([800, 600])
|
|
11
|
+
|
|
12
|
+
put :confirm, :id => resource, :use_route => :fogged
|
|
13
|
+
|
|
14
|
+
assert_json_resource(resource.reload)
|
|
15
|
+
assert_equal 800, resource.width
|
|
16
|
+
assert_equal 600, resource.height
|
|
17
|
+
refute resource.encoding?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test "should confirm video resource with zencoder enabled" do
|
|
21
|
+
in_a_fork do
|
|
22
|
+
require "zencoder"
|
|
23
|
+
require "delayed_job_active_record"
|
|
24
|
+
|
|
25
|
+
Fogged.zencoder_enabled = true
|
|
26
|
+
|
|
27
|
+
Zencoder::Job.expects(:create).returns(
|
|
28
|
+
OpenStruct.new(:body => create_output)
|
|
29
|
+
)
|
|
30
|
+
resource = fogged_resources(:resource_mov)
|
|
31
|
+
|
|
32
|
+
assert_difference("Delayed::Job.count") do
|
|
33
|
+
put :confirm, :id => resource, :use_route => :fogged
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
assert_json_resource(resource.reload)
|
|
37
|
+
assert resource.encoding_job_id
|
|
38
|
+
assert resource.encoding?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test "should not confirm resource with invalid id" do
|
|
43
|
+
assert_raise(ActiveRecord::RecordNotFound) do
|
|
44
|
+
put :confirm, :id => 123456, :use_route => :fogged
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def create_output
|
|
51
|
+
{
|
|
52
|
+
:id => 1234567890
|
|
53
|
+
}.with_indifferent_access
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
module Fogged
|
|
4
|
+
class ResourcesControllerCreateTest < ActionController::TestCase
|
|
5
|
+
tests ResourcesController
|
|
6
|
+
include ResourceTestHelper
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
super
|
|
10
|
+
@resource_params = {
|
|
11
|
+
:name => "Dummy",
|
|
12
|
+
:filename => "dummy.png",
|
|
13
|
+
:content_type => "image/png"
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test "should create resource" do
|
|
18
|
+
assert_difference("Resource.count") do
|
|
19
|
+
post :create, :resource => @resource_params, :use_route => :fogged
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
assert_json_resource(Resource.last)
|
|
23
|
+
assert_equal "png", Resource.last.extension
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test "should not create resource without resource parameter" do
|
|
27
|
+
assert_no_difference("Resource.count") do
|
|
28
|
+
assert_raise(ActionController::ParameterMissing) do
|
|
29
|
+
post :create, :use_route => :fogged
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
%i(filename content_type name).each do |field|
|
|
35
|
+
test "should not create resource without #{field}" do
|
|
36
|
+
assert_no_difference("Resource.count") do
|
|
37
|
+
assert_raise(ActionController::ParameterMissing) do
|
|
38
|
+
post :create,
|
|
39
|
+
:resource => @resource_params.merge(field => ""),
|
|
40
|
+
:use_route => :fogged
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test "should not create resource with invalid filename" do
|
|
47
|
+
assert_no_difference("Resource.count") do
|
|
48
|
+
assert_raise(ActiveRecord::RecordInvalid) do
|
|
49
|
+
post :create,
|
|
50
|
+
:resource => @resource_params.merge(:filename => "bar"),
|
|
51
|
+
:use_route => :fogged
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|