);
61 | }
62 | });
63 |
64 | module.exports = TweetForm;
65 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports and disable caching.
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send.
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger.
20 | config.active_support.deprecation = :log
21 |
22 | # Raise an error on page load if there are pending migrations.
23 | config.active_record.migration_error = :page_load
24 |
25 | # Debug mode disables concatenation and preprocessing of assets.
26 | # This option may cause significant delays in view rendering with a large
27 | # number of complex assets.
28 | config.assets.debug = true
29 |
30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
31 | # yet still be able to expire them through the digest params.
32 | config.assets.digest = true
33 |
34 | # Adds additional error checking when serving assets at runtime.
35 | # Checks for improperly declared sprockets dependencies.
36 | # Raises helpful error messages.
37 | config.assets.raise_runtime_errors = true
38 |
39 | # Raises error for missing translations
40 | # config.action_view.raise_on_missing_translations = true
41 | end
42 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/controllers/tweets_controller.rb:
--------------------------------------------------------------------------------
1 | class TweetsController < ApplicationController
2 |
3 | def index
4 | @tweets = Tweet.all
5 | end
6 |
7 | def show
8 | @tweet = Tweet.find(params[:id])
9 | render :show
10 | end
11 |
12 | def new
13 | @tweet = Tweet.new
14 | render :new
15 | end
16 |
17 | def create
18 | @tweet = Tweet.new(tweet_params)
19 |
20 | if @tweet.save
21 | redirect_to tweet_url(@tweet.id)
22 | else
23 | render :new
24 | end
25 | end
26 |
27 | def edit
28 | @tweet = Tweet.find(params[:id])
29 | render :edit
30 | end
31 |
32 | def update
33 | @tweet = Tweet.find(params[:id])
34 | if @tweet.update(tweet_params)
35 | redirect_to tweet_url(@tweet.id)
36 | else
37 | render :edit
38 | end
39 |
40 | end
41 |
42 | def destroy
43 | Tweet.find(params[:id]).destroy
44 | redirect_to tweets_url
45 | end
46 |
47 |
48 |
49 | private
50 |
51 | def tweet_params
52 | params.require(:tweet).permit(:body, :author_id)
53 | end
54 |
55 |
56 |
57 |
58 | end
59 |
60 |
61 | # NAMESPACING
62 | # Namespace your form data! ie tweet[body]
63 | # {"tweet" => {
64 | # "body" => "Ain't Rails Grand",
65 | # "author_id" => 8
66 | # }
67 | #}
68 |
69 | # STRONG PARAMS
70 | # Use strong params! Tweet.new(params[:tweet]) == security problem.
71 | # famous example that triggered the move to strong_params: the github hack!
72 | # someone set his own user_id on Github to that of an admin.
73 | # check it out here: https://gist.github.com/peternixey/1978249
74 | # strong_params are only relevant for non-GET requests (where you're updating db)
75 | # you have to specifically whitelist params you want to allow
76 |
77 | # WHAT IS PARAMS ANYWAY
78 | # Params: a method that Rails gives you
79 | # returns a "hash-like" object that you can index into just
80 | # like a hash. e.g., to pull out id from params you'll do `params[:id]`
81 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure static file server for tests with Cache-Control for performance.
16 | config.serve_static_files = true
17 | config.static_cache_control = 'public, max-age=3600'
18 |
19 | # Show full error reports and disable caching.
20 | config.consider_all_requests_local = true
21 | config.action_controller.perform_caching = false
22 |
23 | # Raise exceptions instead of rendering exception templates.
24 | config.action_dispatch.show_exceptions = false
25 |
26 | # Disable request forgery protection in test environment.
27 | config.action_controller.allow_forgery_protection = false
28 |
29 | # Tell Action Mailer not to deliver emails to the real world.
30 | # The :test delivery method accumulates sent emails in the
31 | # ActionMailer::Base.deliveries array.
32 | config.action_mailer.delivery_method = :test
33 |
34 | # Randomize the order test cases are executed.
35 | config.active_support.test_order = :random
36 |
37 | # Print deprecation notices to the stderr.
38 | config.active_support.deprecation = :stderr
39 |
40 | # Raises error for missing translations
41 | # config.action_view.raise_on_missing_translations = true
42 | end
43 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended that you check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(version: 20160602145916) do
15 |
16 | # These are extensions that must be enabled in order to support this database
17 | enable_extension "plpgsql"
18 |
19 | create_table "countries", force: :cascade do |t|
20 | t.string "name", null: false
21 | t.datetime "created_at"
22 | t.datetime "updated_at"
23 | end
24 |
25 | create_table "tweets", force: :cascade do |t|
26 | t.string "body", null: false
27 | t.datetime "created_at"
28 | t.datetime "updated_at"
29 | t.integer "author_id", null: false
30 | t.string "image_file_name"
31 | t.string "image_content_type"
32 | t.integer "image_file_size"
33 | t.datetime "image_updated_at"
34 | end
35 |
36 | add_index "tweets", ["author_id"], name: "index_tweets_on_author_id", using: :btree
37 |
38 | create_table "users", force: :cascade do |t|
39 | t.string "name", null: false
40 | t.string "email", null: false
41 | t.datetime "created_at"
42 | t.datetime "updated_at"
43 | t.integer "country_id", null: false
44 | end
45 |
46 | add_index "users", ["country_id"], name: "index_users_on_country_id", using: :btree
47 |
48 | end
49 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # PostgreSQL. Versions 8.2 and up are supported.
2 | #
3 | # Install the pg driver:
4 | # gem install pg
5 | # On OS X with Homebrew:
6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config
7 | # On OS X with MacPorts:
8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
9 | # On Windows:
10 | # gem install pg
11 | # Choose the win32 build.
12 | # Install PostgreSQL and put its /bin directory on your path.
13 | #
14 | # Configure Using Gemfile
15 | # gem 'pg'
16 | #
17 | default: &default
18 | adapter: postgresql
19 | encoding: unicode
20 | # For details on connection pooling, see rails configuration guide
21 | # http://guides.rubyonrails.org/configuring.html#database-pooling
22 | pool: 5
23 |
24 | development:
25 | <<: *default
26 | database: twitter_development
27 |
28 | # The specified database role being used to connect to postgres.
29 | # To create additional roles in postgres see `$ createuser --help`.
30 | # When left blank, postgres will use the default role. This is
31 | # the same name as the operating system user that initialized the database.
32 | #username: twitter
33 |
34 | # The password associated with the postgres role (username).
35 | #password:
36 |
37 | # Connect on a TCP socket. Omitted by default since the client uses a
38 | # domain socket that doesn't need configuration. Windows does not have
39 | # domain sockets, so uncomment these lines.
40 | #host: localhost
41 |
42 | # The TCP port the server listens on. Defaults to 5432.
43 | # If your server runs on a different port number, change accordingly.
44 | #port: 5432
45 |
46 | # Schema search path. The server defaults to $user,public
47 | #schema_search_path: myapp,sharedapp,public
48 |
49 | # Minimum log levels, in increasing order:
50 | # debug5, debug4, debug3, debug2, debug1,
51 | # log, notice, warning, error, fatal, and panic
52 | # Defaults to warning.
53 | #min_messages: notice
54 |
55 | # Warning: The database defined as "test" will be erased and
56 | # re-generated from your development database when you run "rake".
57 | # Do not set this db to the same as development or production.
58 | test:
59 | <<: *default
60 | database: twitter_test
61 |
62 | # As with config/secrets.yml, you never want to store sensitive information,
63 | # like your database password, in your source code. If your source code is
64 | # ever seen by anyone, they now have access to your database.
65 | #
66 | # Instead, provide the password as a unix environment variable when you boot
67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
68 | # for a full rundown on how to provide these environment variables in a
69 | # production deployment.
70 | #
71 | # On Heroku and other platform providers, you may have a full connection URL
72 | # available as an environment variable. For example:
73 | #
74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
75 | #
76 | # You can use this database configuration with:
77 | #
78 | # production:
79 | # url: <%= ENV['DATABASE_URL'] %>
80 | #
81 | production:
82 | <<: *default
83 | database: twitter_production
84 | username: twitter
85 | password: <%= ENV['TWITTER_DATABASE_PASSWORD'] %>
86 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application
18 | # Add `rack-cache` to your Gemfile before enabling this.
19 | # For large-scale production use, consider using a caching reverse proxy like
20 | # NGINX, varnish or squid.
21 | # config.action_dispatch.rack_cache = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Compress JavaScripts and CSS.
28 | config.assets.js_compressor = :uglifier
29 | # config.assets.css_compressor = :sass
30 |
31 | # Do not fallback to assets pipeline if a precompiled asset is missed.
32 | config.assets.compile = false
33 |
34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets,
35 | # yet still be able to expire them through the digest params.
36 | config.assets.digest = true
37 |
38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
39 |
40 | # Specifies the header that your server uses for sending files.
41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
43 |
44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
45 | # config.force_ssl = true
46 |
47 | # Use the lowest log level to ensure availability of diagnostic information
48 | # when problems arise.
49 | config.log_level = :debug
50 |
51 | # Prepend all log lines with the following tags.
52 | # config.log_tags = [ :subdomain, :uuid ]
53 |
54 | # Use a different logger for distributed setups.
55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
56 |
57 | # Use a different cache store in production.
58 | # config.cache_store = :mem_cache_store
59 |
60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
61 | # config.action_controller.asset_host = 'http://assets.example.com'
62 |
63 | # Ignore bad email addresses and do not raise email delivery errors.
64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65 | # config.action_mailer.raise_delivery_errors = false
66 |
67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68 | # the I18n.default_locale when a translation cannot be found).
69 | config.i18n.fallbacks = true
70 |
71 | # Send deprecation notices to registered listeners.
72 | config.active_support.deprecation = :notify
73 |
74 | # Use default logging formatter so that PID and timestamp are not suppressed.
75 | config.log_formatter = ::Logger::Formatter.new
76 |
77 | # Do not dump schema after migrations.
78 | config.active_record.dump_schema_after_migration = false
79 | end
80 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actionmailer (4.2.6)
5 | actionpack (= 4.2.6)
6 | actionview (= 4.2.6)
7 | activejob (= 4.2.6)
8 | mail (~> 2.5, >= 2.5.4)
9 | rails-dom-testing (~> 1.0, >= 1.0.5)
10 | actionpack (4.2.6)
11 | actionview (= 4.2.6)
12 | activesupport (= 4.2.6)
13 | rack (~> 1.6)
14 | rack-test (~> 0.6.2)
15 | rails-dom-testing (~> 1.0, >= 1.0.5)
16 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
17 | actionview (4.2.6)
18 | activesupport (= 4.2.6)
19 | builder (~> 3.1)
20 | erubis (~> 2.7.0)
21 | rails-dom-testing (~> 1.0, >= 1.0.5)
22 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
23 | activejob (4.2.6)
24 | activesupport (= 4.2.6)
25 | globalid (>= 0.3.0)
26 | activemodel (4.2.6)
27 | activesupport (= 4.2.6)
28 | builder (~> 3.1)
29 | activerecord (4.2.6)
30 | activemodel (= 4.2.6)
31 | activesupport (= 4.2.6)
32 | arel (~> 6.0)
33 | activesupport (4.2.6)
34 | i18n (~> 0.7)
35 | json (~> 1.7, >= 1.7.7)
36 | minitest (~> 5.1)
37 | thread_safe (~> 0.3, >= 0.3.4)
38 | tzinfo (~> 1.1)
39 | annotate (2.6.5)
40 | activerecord (>= 2.3.0)
41 | rake (>= 0.8.7)
42 | arel (6.0.3)
43 | aws-sdk (2.2.31)
44 | aws-sdk-resources (= 2.2.31)
45 | aws-sdk-core (2.2.31)
46 | jmespath (~> 1.0)
47 | aws-sdk-resources (2.2.31)
48 | aws-sdk-core (= 2.2.31)
49 | bcrypt (3.1.11)
50 | better_errors (2.1.1)
51 | coderay (>= 1.0.0)
52 | erubis (>= 2.6.6)
53 | rack (>= 0.9.0)
54 | binding_of_caller (0.7.2)
55 | debug_inspector (>= 0.0.1)
56 | builder (3.2.2)
57 | byebug (8.2.5)
58 | climate_control (0.0.3)
59 | activesupport (>= 3.0)
60 | cocaine (0.5.8)
61 | climate_control (>= 0.0.3, < 1.0)
62 | coderay (1.1.1)
63 | coffee-rails (4.1.1)
64 | coffee-script (>= 2.2.0)
65 | railties (>= 4.0.0, < 5.1.x)
66 | coffee-script (2.4.1)
67 | coffee-script-source
68 | execjs
69 | coffee-script-source (1.10.0)
70 | concurrent-ruby (1.0.1)
71 | debug_inspector (0.0.2)
72 | erubis (2.7.0)
73 | execjs (2.6.0)
74 | faker (1.6.3)
75 | i18n (~> 0.5)
76 | figaro (1.1.1)
77 | thor (~> 0.14)
78 | globalid (0.3.6)
79 | activesupport (>= 4.1.0)
80 | i18n (0.7.0)
81 | jbuilder (2.4.1)
82 | activesupport (>= 3.0.0, < 5.1)
83 | multi_json (~> 1.2)
84 | jmespath (1.1.3)
85 | jquery-rails (4.1.1)
86 | rails-dom-testing (>= 1, < 3)
87 | railties (>= 4.2.0)
88 | thor (>= 0.14, < 2.0)
89 | json (1.8.3)
90 | loofah (2.0.3)
91 | nokogiri (>= 1.5.9)
92 | mail (2.6.4)
93 | mime-types (>= 1.16, < 4)
94 | method_source (0.8.2)
95 | mime-types (3.0)
96 | mime-types-data (~> 3.2015)
97 | mime-types-data (3.2016.0221)
98 | mimemagic (0.3.1)
99 | mini_portile2 (2.0.0)
100 | minitest (5.8.4)
101 | multi_json (1.11.3)
102 | nokogiri (1.6.7.2)
103 | mini_portile2 (~> 2.0.0.rc2)
104 | paperclip (5.0.0.beta1)
105 | activemodel (>= 4.2.0)
106 | activesupport (>= 4.2.0)
107 | cocaine (~> 0.5.5)
108 | mime-types
109 | mimemagic (~> 0.3.0)
110 | pg (0.18.4)
111 | pry (0.10.3)
112 | coderay (~> 1.1.0)
113 | method_source (~> 0.8.1)
114 | slop (~> 3.4)
115 | pry-rails (0.3.4)
116 | pry (>= 0.9.10)
117 | rack (1.6.4)
118 | rack-test (0.6.3)
119 | rack (>= 1.0)
120 | rails (4.2.6)
121 | actionmailer (= 4.2.6)
122 | actionpack (= 4.2.6)
123 | actionview (= 4.2.6)
124 | activejob (= 4.2.6)
125 | activemodel (= 4.2.6)
126 | activerecord (= 4.2.6)
127 | activesupport (= 4.2.6)
128 | bundler (>= 1.3.0, < 2.0)
129 | railties (= 4.2.6)
130 | sprockets-rails
131 | rails-deprecated_sanitizer (1.0.3)
132 | activesupport (>= 4.2.0.alpha)
133 | rails-dom-testing (1.0.7)
134 | activesupport (>= 4.2.0.beta, < 5.0)
135 | nokogiri (~> 1.6.0)
136 | rails-deprecated_sanitizer (>= 1.0.1)
137 | rails-html-sanitizer (1.0.3)
138 | loofah (~> 2.0)
139 | railties (4.2.6)
140 | actionpack (= 4.2.6)
141 | activesupport (= 4.2.6)
142 | rake (>= 0.8.7)
143 | thor (>= 0.18.1, < 2.0)
144 | rake (11.1.2)
145 | rdoc (4.2.2)
146 | json (~> 1.4)
147 | sass (3.4.22)
148 | sass-rails (5.0.4)
149 | railties (>= 4.0.0, < 5.0)
150 | sass (~> 3.1)
151 | sprockets (>= 2.8, < 4.0)
152 | sprockets-rails (>= 2.0, < 4.0)
153 | tilt (>= 1.1, < 3)
154 | sdoc (0.4.1)
155 | json (~> 1.7, >= 1.7.7)
156 | rdoc (~> 4.0)
157 | slop (3.6.0)
158 | spring (1.7.1)
159 | sprockets (3.6.0)
160 | concurrent-ruby (~> 1.0)
161 | rack (> 1, < 3)
162 | sprockets-rails (3.0.4)
163 | actionpack (>= 4.0)
164 | activesupport (>= 4.0)
165 | sprockets (>= 3.0.0)
166 | thor (0.19.1)
167 | thread_safe (0.3.5)
168 | tilt (2.0.2)
169 | turbolinks (2.5.3)
170 | coffee-rails
171 | tzinfo (1.2.2)
172 | thread_safe (~> 0.1)
173 | uglifier (3.0.0)
174 | execjs (>= 0.3.0, < 3)
175 | web-console (2.3.0)
176 | activemodel (>= 4.0)
177 | binding_of_caller (>= 0.7.2)
178 | railties (>= 4.0)
179 | sprockets-rails (>= 2.0, < 4.0)
180 |
181 | PLATFORMS
182 | ruby
183 |
184 | DEPENDENCIES
185 | annotate
186 | aws-sdk (>= 2.0)
187 | bcrypt (~> 3.1.7)
188 | better_errors
189 | binding_of_caller
190 | byebug
191 | coffee-rails (~> 4.1.0)
192 | faker
193 | figaro
194 | jbuilder (~> 2.0)
195 | jquery-rails
196 | paperclip (= 5.0.0.beta1)
197 | pg (~> 0.15)
198 | pry-rails
199 | rails (= 4.2.6)
200 | sass-rails (~> 5.0)
201 | sdoc (~> 0.4.0)
202 | spring
203 | turbolinks
204 | uglifier (>= 1.3.0)
205 | web-console (~> 2.0)
206 |
207 | BUNDLED WITH
208 | 1.11.2
209 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React File Upload Demo
2 | This demo shows how to upload images using React, Paperclip, and AWS S3.
3 |
4 | # Video Demo
5 | - [Part One (paperclip, aws)](https://vimeo.com/169111348)
6 | - [Part Two (uploading files via a form)](https://vimeo.com/169111248)
7 |
8 | ## Key Files
9 | - [application.rb](./config/application.rb)
10 | - [tweet.rb](./app/models/tweet.rb)
11 | - [_tweet.json.jbuilder](./app/views/api/tweets/_tweet.json.jbuilder)
12 | - [TweetApi.js](./frontend/utils/TweetApi.js)
13 | - [TweetForm.jsx](./frontend/components/TweetForm.jsx)
14 |
15 | ## Useful Docs
16 | - [Paperclip](https://github.com/thoughtbot/paperclip#paperclip)
17 | - [Figaro] (https://github.com/laserlemon/figaro#why-does-figaro-exist)
18 | - [AWS] (http://aws.amazon.com/)
19 | - [FileReader] (https://developer.mozilla.org/en-US/docs/Web/API/FileReader)
20 | - [FormData] (https://developer.mozilla.org/en-US/docs/Web/API/FormData)
21 |
22 | ### Setting up AWS
23 |
24 | - The first thing we need to set up is our buckets. This is where amazon will actually store our files. Click on 'S3' and then 'Create Bucket'. We should make a separate bucket for development and production. I would use something like `app-name-dev`, and `app-name-pro`. Set the region to 'US Standard'.
25 | - Now we have space set aside on AWS, but we don't have permission to access it. We need to create a user, and a policy for them to access your buckets. Go back to the main page and click 'Identity and Access Managment' then click 'Users' on the left. We'll make a new user, named whatever you like.
26 | - You'll be directed to a page with your brand new security credentials, DOWNLOAD AND SAVE THEM NOW, you will not have access to them again. If you do lose them, just delete the user and make a new one.
27 | - The keys you just saved give you access to your AWS server space, **don't give push them to github, or put them anywhere public!**
28 | - Now we need to set up the security policy for our new user. This is how they will be allowed to connect. Click 'Inline Policies' and then create one, then choose 'Custom Policy'. You can use this sensible default and not worry too much about what it's doing for you (borrrrriing). Remember to switch out bucket-name for your bucket.
29 |
30 | ```json
31 | {
32 | "Version": "2012-10-17",
33 | "Statement": [
34 | {
35 | "Sid": "Stmt1420751757000",
36 | "Effect": "Allow",
37 | "Action": [
38 | "s3:*"
39 | ],
40 | "Resource": [
41 | "arn:aws:s3:::BUCKET-NAME-DEV",
42 | "arn:aws:s3:::BUCKET-NAME-DEV/*",
43 | "arn:aws:s3:::BUCKET-NAME-PRO",
44 | "arn:aws:s3:::BUCKET-NAME-PRO/*"
45 | ]
46 | }
47 | ]
48 | }
49 | ```
50 | - That's pretty much it for AWS. Now we have to convince paperclip to use it!
51 |
52 | ### Setting up Paperclip
53 |
54 | - ImageMagick is a dependency of paperclip. It is installed on the a/A machines but you will need to install it at home. `brew install imagemagick`
55 | - Add the gem of course `gem "paperclip", '~> 5.0.0'`. The video references the beta because it was the only version compatible with the latest version of AWS at the time it was filmed, but you should not need the beta anymore.
56 | - We need to create a migration to add the attached file columns. We'll add them to posts for the demo. `rails generate paperclip post image`
57 | - We also need to add code to the model to tell it how to handle attached files. Check the [Paperclip docs](https://github.com/thoughtbot/paperclip#paperclip) for more info!
58 |
59 | ```ruby
60 | class Post < ActiveRecord::Base
61 | has_attached_file :image, default_url: "missing.png"
62 | validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
63 | end
64 | ```
65 |
66 | - Great, lastly we need to set up paperclip to save to AWS. Before we do this we need somewhere safe to store our secret access keys. Enter figaro!
67 | - Add `gem 'figaro'` and then run `bundle exec figaro install`
68 | - Figaro has created a new application.yml file and added it to your gitignore. All your secret app keys can be stored in this file, and we will reference them using syntax like `ENV["secret_key"]` throughout our app.
69 | - Be careful to save this file to your email or dropbox, because it will not be pushed to github.
70 | - Double check that application.yml is gitignored. **People will scrape github for S3 keys and exploit your account if they can.**
71 | - Now we can add our secret keys. It should look something like this.
72 | ```ruby
73 |
74 | # config/application.yml
75 | development:
76 | s3_bucket: "BUCKET-NAME-DEV"
77 |
78 | production:
79 | s3_bucket: "BUCKET-NAME-PRO"
80 |
81 | s3_region: "us-east-1"
82 | s3_access_key_id: "XXXX"
83 | s3_secret_access_key: "XXXX"
84 | ```
85 |
86 | - Now that we have a safe way to access our secret keys, we need to update our application.rb file to configure paperclip to use s3. Let's add one more gem for this. `gem 'aws-sdk', '>= 2.0'`
87 |
88 | ```ruby
89 | # config/application.rb
90 |
91 | config.paperclip_defaults = {
92 | :storage => :s3,
93 | :s3_credentials => {
94 | :bucket => ENV["s3_bucket"],
95 | :access_key_id => ENV["s3_access_key_id"],
96 | :secret_access_key => ENV["s3_secret_access_key"],
97 | :s3_region => ENV["s3_region"]
98 | }
99 | }
100 | ```
101 | - We did it! You should be able to attach files through the console, test it out.
102 | ```ruby
103 | post = Post.first
104 | file = File.open('app/assets/images/sennacy.jpg')
105 | post.image = file
106 | post.save!
107 | post.image.url #=> "http://s3.amazonaws.com/YOUR-BUCKET-NAME/something/images/000/000/607/sennacy.jpg?1459267299"
108 | ```
109 |
110 | ### Image Preview
111 | - Okay so what if we don't want our users to upload files via rails console? We need to be able to attach files from a form. Lets add something to our post form.
112 | - To preview the file, we need to extract a url for it. On change of the file input component we instantiate a new [FileReader]
113 | (https://developer.mozilla.org/en-US/docs/Web/API/FileReader) object. set a success function for when it loads
114 | Then we ask it to read the file `reader.readAsDataURL(file);`
115 | (https://developer.mozilla.org/en-US/docs/Web/API/FileReader.readAsDataURL)
116 | ```javascript
117 | var reader = new FileReader();
118 | var file = e.currentTarget.files[0];
119 | reader.onloadend = function() {
120 | this.setState({ imageUrl: reader.result, imageFile: file});
121 | }.bind(this);
122 |
123 | if (file) {
124 | reader.readAsDataURL(file);
125 | } else {
126 | this.setState({ imageUrl: "", imageFile: null });
127 | }
128 | ```
129 | - Once it's loaded we can preview the image using the imageUrl we saved in state. Awesome!
130 |
131 | ### Image Uploading
132 | - We still haven't sent the file to the server to be saved. To upload the file we will instantiate a new
133 | [FormData] (https://developer.mozilla.org/en-US/docs/Web/API/FormData) object.
134 | We then use the [append](https://developer.mozilla.org/en-US/docs/Web/API/FormData/append)
135 | method to add key/values to send to the server. One of the key/value pairs will be the binary
136 | file we grab from `this.state.file`. Be mindful to have your keys match whatever your Rails
137 | controller is expecting in the params. In our case this is `post[image]`.
138 | ```javascript
139 | var file = this.state.imageFile;
140 |
141 | var formData = new FormData();
142 | formData.append("post[title]", title);
143 | formData.append("post[image]", file);
144 |
145 | ApiUtil.createPost(formData, this.resetForm);
146 | ```
147 |
148 | We will use
149 | `ApiUtil.createPost()` to make the AJAX request and create an action on success. In the
150 | options for the `$.ajax` request we need to set `processData` and `contentType` both to
151 | `false`. This is to prevent default jQuery behaviour from trying to convert our FormData
152 | object and sending up the wrong header. See more in this [SO post](http://stackoverflow.com/a/8244082).
153 |
154 | ```javascript
155 | createPost: function(formData) {
156 | $.ajax({
157 | url: '/api/posts',
158 | type: 'POST',
159 | processData: false,
160 | contentType: false,
161 | dataType: 'json',
162 | data: formData,
163 | success: function(post) {
164 | PostActions.receivePost(post);
165 | }
166 | })
167 | }
168 | ```
169 |
170 | ### Image Missing and Jbuilder
171 | - Once our images are saving successfully the last step is to display them when they are retrieved from the database. We can use the `post.image.url` method in our jbuilder template and then use that value as the src to an image tag. But we also need to remember to use the `asset_path` helper to make sure our path is set correctly. You'll probably end up with something like this.
172 | `json.image_url asset_path(post.image.url(:original))`. This will catch your default image url as well if it's in assets/images.
173 |
174 | ### Configuring for production
175 | - Last step, heroku won't have our application.yml (it's in our gitignore!) So we need to send up the keys. You can use this convenient figaro commend.
176 | ```
177 | $ figaro heroku:set -e production
178 | ```
179 | - Congrats! You can did it!
180 |
--------------------------------------------------------------------------------