We have written an extensive blog
on what CSRF is and what steps Rails 4 takes to prevent CSRF. We
encourage you to read that blog to fully understand rest of this article.
Nested form can get around CSRF protection offered by Rails 4
A typical form generated in Rails 4 might look like this.
Since nested forms are not allowed browser will accept the top most
level form. In this case that happens to be the form created by the
hacker. When this form is submitted then “authenticity_token” is also
submitted and Rails will do its check and will say everything is looking
good and thus hacker will be able to hack the site.
Rails 5 fixes the issue by generating a custom token for a form
Adding that code to each controller feels burdensome. In that case you
can enable this behavior for all controllers in the application by
adding following line to an initializer.
This will add authenticity token specific to method and action in each form tag of the application.
After adding that token the generated form might look like as shown
below.
Authenticity token included here will be specific to action money_transfer and method post.
Attacker can still grab authenticity_token here, but attack will be limited to money_transfer post action.
Rails request-response cycle is very easy to understand.
A request hits the app, a route is matched to a controller action from routes.rb,
and finally controller action processes the request and renders HTML or JSON based
on the type of the request.
But sometimes we want to render our HTML or JSON response outside of this request-response
cycle.
For example let’s say user is allowed to download PDF version of a
report on web. This can be done using request-response cycle. We also
need to send a weekly report to managers and the email should have the
report as an attachment. Now we need to generate the same PDF but since
emails are sent using background job the request-response cycle is
missing.
Rails 5 has this feature baked in.
Let’s say we have a OrdersController and
we want to render individual order outside of controller.
Fire up rails console and execute following command.
This will render app/views/orders/show.html.erb with @order set to
Order.last. Instance variables can be set using
assigns in the same way
we use them in controller actions.
Those instance variables will be passed to
the view that is going to be rendered.
This will render app/views/orders/_form.html.erb and will pass order as local
variable.
Say I want to render all orders, but in JSON format.
OrdersController.render json: Order.all
# => "[{"id":1, "name":"The Well-Grounded Rubyist", "author":"David A. Black"},
{"id":2, "name":"Remote: Office not required", "author":"David & Jason"}]
Even rendering simple text is possible.
>>BooksController.renderplain: 'this is awesome!'Renderedtexttemplate(0.0ms)# => "this is awesome!"
Similar to text, we can also use render file and render template.
Request environment
A typical web request carries it’s own environment with it. We usually handle
this environment using request.env in controllers.
Certain gems like devise depends on env hash for information such as warden token.
So when we are rendering outside of controller,
we need to make sure that
the rendering happens with correct environment.
Rails provides a default rack environment for this purpose.
The default options used
can be accessed through renderer.defaults.
Internally, Rails will build a new Rack environment based on these options.
Customizing the environment
We can customize environment using method renderer.
Let’s say that we need “method as post” and we want
“https to be true” for our background job processing.
Recently we came across following error
when mobile app was trying to upload images using backend API.
==> nginx-error.log <==
2015/12/16 07:34:13 [error] 1440#0: *1021901
sendfile() failed (32: Broken pipe)while sending request to upstream,
client: xxx.xx.xxx.xx, server: app-name.local,
request: "POST /api_endpoint HTTP/1.1",
upstream: "http://unix:/data/apps/app-name/shared/tmp/sockets/unicorn.sock:/api_endpoint",
host: "appname.com"==> nginx-error.log <==
xxx.xx.xxx.xx - - [16/Dec/2015:07:34:13 - 0500]
"POST /api_endpoint HTTP/1.1" 502 1051 "-""Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13B143 (366691552)"
The backend API was developed using Ruby on Rails and was powered using NGINX HTTP server along with unicorn application server.
Notice the the http response received was 502 which is documented in
RFC as shown below.
10.5.3 502 Bad Gateway
The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.
Debugging the problem
From the first look it seemed that the client_max_body_size parameter for nginx was too less,
which might cause the request to fail if the uploaded filesize is greater than the allowed limit.
But that was not the case with our NGINX configuration.
The NGINX was configured to accept file size upto 250 megabytes, while the file being uploaded was around 6 megabytes.
In our Rails code we are forcing all traffic to use HTTPS by having production.rb to have following setting.
config.force_ssl=true
What this setting does is, it forces HTTPS by redirecting HTTP requests to their HTTPS counterparts.
So any request accessing http://domain.com/path will be redirected to https://domain.com/path
Forcing all traffic to HTTPS still does not explain the vague error sendfile() failed (32: Broken pipe) while sending request to upstream and why the HTTP 502 error which means Bad Gateway
Here is how our nginx.conf is configured.
server {
...
listen 80;
listen 443 ssl;
...
}
NGINX is configured to accept both HTTP and HTTPS requests. This is a
pretty standard setting so that application can support both
HTTP and HTTPS.
What it means is that nginx will accept both HTTP and HTTPS request.
It would not force HTTP request to be HTTPS.
It would forward HTTP request to rails backend api.
Here is what happens when you upload a file
When we upload a file then NGINX takes that request and passes that
request to Rails. NGINX expects a response from Rails only when
NGINX is done sending the whole request.
Let’s see what happens when a user visits login page using HTTP. NGINX
takes the request and passes the request to Rails. When NGINX is done
handing over the request to Rails then NGINX expects a response from
Rails. However in this case rather than sending 200 Rails sends a
redirect over HTTPS. So now NGINX takes up the request again over HTTPS
and then hands over request to Rails. Rails processes the request and
returns 200. Everything works out.
Now let’s see what happens when a file is uploaded over HTTP.
User uploads a file over HTTP. NGINX takes up the request and starts
sending the file to Rails. More details about the file upload process
can be see at RFC. The data is
sent to server in chunks. When first chunk is sent to server then server
notices that data is being sent over HTTP and it immediately sends a
response that data should be sent over HTTPS.
In the meantime on the client side , client is still pushing rest of the
data to the server. Client expects a response from the server only when
it is done pushing all the data. However in this case server sends a
response when client is not expecting it.
NGINX at this time is all confused and thinks something has gone wrong
with the gateway and returns HTTP/1.1 502 Bad Gateway and aborts the
upload operation. And that how we get the 502 error.
So next time you see an error like this make sure that you are using
https to upload the file if server is enforcing https.
We at BigBinary adopted ReactJS pretty early. Early in the year we
published series of videos titled Learn ReactJS in steps
which takes a “Hello World” app into a full TODO application using
ReactJS in incremental steps.
We also started an ios app using React Native. It’s coming along pretty
good and soon we should see it in app store.
We authored many blogs
We love sharing our experiences via blogs on various topics
like ReactJS, Rails, React Native and Robot framework etc.
Here are some of our blogs from 2015.
Apart from our team members contributing to various OpenSource projects, we also support some projects from our team.
This year, we added and helped build following projects-
Wheel : Wheel is our Rails template for new Ruby on Rails projects, with sane defaults and setups for different environments, and common functionalities like image uploaders, debugging, etc.
Mail Interceptor : Interception, Forwarding emails in Ruby on Rails application
Handy : A collection handy tools and Rails tasks for your Project.
Fixtures Dumper : Dump your Rails data to fixtures easily to a database to populate data.
Community Engagement
Along with speaking at various conferences, we also helped organize, our Fun edition of Pune’s regional RubyConf,
DeccanRubyConf,
and supported other Indian Conferences,
including RubyConfIndia, GardenCity RubyConf.
We also help Pune’s local ruby meetup, which had a splendid engagement this year.
Overall we are super excited about what we accomplished in year 2015.
We are looking forward to an exciting year of 2016!
If you run bin/rails -h in a Rails 5 app, you will see a new command for running tests.
$ bin/rails -h
Usage: rails COMMAND [ARGS]
The most common rails commands are:
generate Generate new code (short-cut alias: "g")
console Start the Rails console (short-cut alias: "c")
server Start the Rails server (short-cut alias: "s")test Run tests (short-cut alias: "t")
Before Rails 5, we had to use bin/rake test to run tests.
But in Rails 5, we can use bin/rails test.
It is not just replacement of old rake task but it is backed by a test runner
inspired from RSpec, minitest-reporters, maxitest and others.
Let’s see what bin/rails test can do.
Running a single test
Now it is possible to run a single test out of the box using the
line number of the test.
$ bin/rails test test/models/user_test.rb:27
Rails will intelligently run the test from user_test.rb having line number 27.
Note that the line number 27 does not need to be first line of the test.
Below is an example.
In the above case, test_valid_user can be run as long as the line number
provided is between 22 and 30.
Running multiple tests
You can also pass multiple test paths and Rails will run all of those tests.
$ bin/rails test test/models/user_test.rb:27 test/models/post_test.rb:42
It is also possible to run all tests from a directory.
$ bin/rails test test/controllers test/integration
Improved failure messages
When a test fails, Rails displays a command which can be used to just run
the failed test.
$ bin/rails t
Run options: --seed 51858
# Running:
.F
Failure:
PostsControllerTest#test_should_get_new:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>
bin/rails test test/controllers/posts_controller_test.rb:15
I can simply copy
bin/rails test test/controllers/posts_controller_test.rb:15
and rerun the failing test.
Failing fast
By default when a test fails then rails reports about the test failures
and then moves on to the next test. If you want to stop running the test
when a test fails then use option -f.
$ bin/rails t -f
Run options: -f--seed 59599
# Running:
..F
Failure:
PostsControllerTest#test_should_get_new:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>
bin/rails test test/controllers/posts_controller_test.rb:15
Interrupted. Exiting...
Finished in 0.179125s, 16.7481 runs/s, 22.3308 assertions/s.
3 runs, 4 assertions, 1 failures, 0 errors, 0 skips
Defer test output until the end of the full test run
By default when a test fails then rails prints F and then details
about the failure like what assertion failed and how to re run the test
etc.
If you want to have a clean output of . and F and would like all the
test failures report to come at the every end then use option -d.
$ bin/rails t -d
Run options: -d --seed 29906
# Running:
..F...F
Finished in 0.201320s, 34.7704 runs/s, 49.6721 assertions/s.
1) Failure:
PostsControllerTest#test_should_create_post [/Users/prathamesh/Projects/fun/rails-5-test-runner-app/test/controllers/posts_controller_test.rb:19]:
"Post.count" didn't change by 1.
Expected: 3
Actual: 2
2) Failure:
PostsControllerTest#test_should_get_new [/Users/prathamesh/Projects/fun/rails-5-test-runner-app/test/controllers/posts_controller_test.rb:15]:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>
7 runs, 10 assertions, 2 failures, 0 errors, 0 skips
Failed tests:
bin/rails test test/controllers/posts_controller_test.rb:19
bin/rails test test/controllers/posts_controller_test.rb:15
Better backtrace output
By default when an error is encountered while running the test
then the output does not contain full stacktrace.
This makes debugging little bit difficult.
Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007f86bc62b728>
app/controllers/posts_controller.rb:29:in `create'
test/controllers/posts_controller_test.rb:20:in `block (2 levels) in <class:PostsControllerTest>'
test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest
Now we can use -b switch,
which will display complete backtrace of error message.
$ bin/rails t -b
Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007fc53c4eb868>
/rails-5-test-runner-app/app/controllers/posts_controller.rb:29:in `create'
/sources/rails/actionpack/lib/action_controller/metal/basic_implicit_render.rb:4:in `send_action'
/sources/rails/actionpack/lib/abstract_controller/base.rb:183:in `process_action'
/sources/rails/actionpack/lib/action_controller/metal/rendering.rb:30:in `process_action'
/sources/rails/actionpack/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
/sources/rails/activesupport/lib/active_support/callbacks.rb:126:in `call'
.....
/sources/rails/activesupport/lib/active_support/testing/assertions.rb:71:in `assert_difference'
/rails-5-test-runner-app/test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest>'
Leveraging power of Minitest
The test runner also leverages power of minitest by providing some handy options.
Switch -s to provide your own seed
Now we can also provide our own seed using -s switch.
$ bin/rails t --s 42000
Switch -n to run matching tests
Switch -n will run tests matching
the given string or regular expression pattern.
$ bin/rails t -n "/create/"
Run options: -n /create/ --seed 24558
# Running:
E
Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007faa39c2df90>
app/controllers/posts_controller.rb:29:in `create'
test/controllers/posts_controller_test.rb:20:in `block (2 levels) in <class:PostsControllerTest>'
test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest>'
bin/rails test test/controllers/posts_controller_test.rb:18
Finished in 0.073857s, 13.5396 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
Verbose output
It is also possible to see the verbose output using -v switch.
It shows time required to run each test.
This would help in detecting slow running tests.
$ bin/rails t -v
Run options: -v --seed 30118
# Running:
PostsControllerTest#test_should_destroy_post = 0.07 s = .
PostsControllerTest#test_should_update_post = 0.01 s = .
PostsControllerTest#test_should_show_post = 0.10 s = .
PostsControllerTest#test_should_create_post = 0.00 s = F
Failure:
PostsControllerTest#test_should_create_post:
"Post.count" didn't change by 1.
Expected: 3
Actual: 2
bin/rails test test/controllers/posts_controller_test.rb:19
PostsControllerTest#test_should_get_new = 0.02 s = .
PostsControllerTest#test_should_get_index = 0.01 s = .
PostsControllerTest#test_should_get_edit = 0.00 s = .
Finished in 0.210071s, 33.3220 runs/s, 47.6028 assertions/s.
7 runs, 10 assertions, 1 failures, 0 errors, 0 skips
Colored output
Now by default we will get colored output.
No need to add additional gem to colored output.
With all these awesome features, testing Rails 5 apps has definitely become
a better experience.
Rails has shipped all these features within
the framework itself so you don’t have to use multiple gems and libraries to achieve
all of these things.
This behavior is similar to how controllers inherit from ApplicationController
instead of inheriting from ActionController::Base.
Now ApplicationRecord will be a single point of entry for all the customizations and
extensions needed for an application, instead of monkey patching ActiveRecord::Base.
Say I want to add some extra functionality to Active Record.
This is what I would do in Rails 4.2.
But now, ActiveRecord::Base forever includes MyAwesomeFeature and any class
inheriting from it also includes MyAwesomeFeature even if they don’t want it.
This is especially true if you are using plugins and engines where monkey patching to ActiveRecord::Base
can leak into engine or plugin code.
But with ApplicationRecord, they will be localized to only those models which
are inheriting from ApplicationRecord, effectively only to your application.
By default all new Rails 5 applications will have application_record.rb.
If you are migrating from Rails 4, then simply create app/models/application_record.rb as shown below
and change all models to inherit from ApplicationRecord instead of ActiveRecord::Base.
When a Rails application involving multiple gems, engines etc. are built then it’s important
to know how constants are looked up and resolved.
Consider a brand new Rails app with model User.
classUserdefself.model_method'I am in models directory'endend
Run User.model_method in rails console. It runs as expected.
Now add file user.rb in lib directory.
classUserdefself.lib_method'I am in lib directory'endend
Reload rails console and try executing User.model_method and User.lib_method.
You will notice that User.model_method gets executed and User.lib_method doesn’t. Why is that?
In Rails we do not import files
If you have worked in other programming languages like Python or Java
then in your file you must have statements to import other files. The
code might look like this.
In Rails we do not do that. That’s because
DHH
does not like the idea of opening a file and seeing the top of the file
littered with import statements.
He likes to see his files beautiful.
Since we do not import file then how does it work?
In Rails console when user types User then rails detects that User
constant is not loaded yet.
So it needs to load User constant.
However in order to do that it has to load a file.
What should be the
name of the file. Here is what Rails does.
Since the constant name is User Rails says that I’m going to look for file user.rb.
So now we know that we are looking for user.rb file. But the
question is where to look for that file. Rails has autoload_path. As
the name suggests this is a list of paths from where files are
automatically loaded. Rails will search for user.rb in this list of
directories.
As you can see in the result one of the folders is app/models. When
Rails looks for file user.rb in app/models then Rails will find it
and it will load that file.
That’s how Rails loads User in Rails console.
Adding lib to the autoload path
Let’s try to load User from lib directory.
Open config/application.rb and add following code in the
initialization part.
config.autoload_paths += ["#{Rails.root}/lib"]
Now exit rails console and restart it.
And now lets try to execute the same command.
Here you can see that lib directory has been added at the very top.
Rails goes from top to bottom while looking for user.rb file. In this
case Rails will find user.rb in lib and Rails will stop looking for
user.rb. So the end result is that user.rb in app/models directory
would not even get loaded as if it never existed.
Enhancing a model
Here we are trying to add an extra method to User model. If we stick
our file in lib then our user.rb is never loaded because Rails will
never look for anything in lib by default. If we ask Rails to look in
lib then Rails will not load file from app/models because the file
is already loaded. So how do we enhance a model without sticking code in
app/models/user.rb file.
Introducing initializer to load files from model and lib directories
We need some way to load User from both models and lib directories.
This can be done by adding an
initializer to config/initializers directory with following code snippet
Now both User.model_method and User.lib_method get executed as expected.
In the above case when first time user.rb is loaded then constant
User gets defined. Second time ruby understands that constant is
already defined so it does not bother defining it again. However it adds
additional method lib_method to the constant.
In that above case if we replace load file with require file then
User.lib_method will not work. That is because require will not load
a file if a constant is already defined. Read
here
and
here
to learn about how load and require differ.
Using ‘require_relative’ in model
Another approach of solving this issue is by using require_relative inside model. require_relative loads the file
present in the path that is relative to the file where the statement is called in. The desired file to be loaded is given
as an argument to require_relative
In our example, to have User.lib_method successfully executed, we need to load the lib/user.rb. Adding the following code
in the beginning of the model file user.rb should solve the problem. This is how app/models/user.rb will now look like.
require_relative'../../lib/user'classUserdefself.model_method'I am in models directory'endend
Here require_relative upon getting executed will first initialize the constant User from lib directory.
What follows next is opening of the same class User that has been initialized already and addition of model_method to it.
Handling priorities between Engine and App
In one of the projects we are using engines.
SaleEngine has a model Sale. However Sale doesn’t get resolved as
path for engine is neither present in config.autoload_paths nor in ActiveSupport::Dependencies.autoload_paths.
The initialization of engine happens in engine.rb file present inside lib directory of the engine.
Let’s add a line to load engine.rb inside application.rb file.
In Rails console if we try to see autoload path then we will see that
lib/sale_engine is present there. That means we can now use
SaleEngine::Engine.
Now any file we add in sale_engine directory would be loaded. However
if we add user.rb here then the user.rb mentioned in app/models
would be loaded first because the application directories have
precedence. The precedence order can be changed by following statements.
engines=[SaleEngine::Engine]# in case there are multiple enginesconfig.railties_order=engines+[:main_app]
The symbol :main_app refers to the application where the server comes up. After adding the above code, you will see that
the output of ActiveSupport::Dependencies now shows the directories of engines first (in the order in which they have been
given) and then those of the application. Hence for any class which is common between your app and engine, the one from
engine will now start getting resolved. You can experiment by adding multiple engines and changing the railties_order.
Further reading
Loading of constants is a big topic and Xavier Noria from Rails core team has made some excellent presentations. Here are some of them
I added stripe to my React Native application using following command.
npm install stripe --save
Added Stripe to the project as shown below.
var stripe = require('stripe')('stripe API key');
And now I’m getting following error.
This’s because npm package uses
stripe.js
and
stripe.js needs
http
provided by Node.JS.
Our React Native code does not actually run on Node.JS.
I know while building React Native
we use NPM
and
we have a node server running in the background
so it feels like
we are in Node.JS land
and
we can use
everything Node.JS has to offer.
But that’s not true.
React Native code can’t depend on Node.JS
because
Node.JS is not shipped with ios.
It means
we can’t use any of Node.JS packages including http.
It means
we can’t use stripe npm module.
Now you can remove
require('stripe') line that was added
in the last step.
Solution
React Native comes with
Fetchapi.
From the documentation
The Fetch API provides a JavaScript interface for accessing an manipulating parts of the HTTP pipeline,
such as requests and responses. It also provides a global fetch() method that provides an easy,
logical way to fetch resources asynchronously across the network.
stripe.js is a nice wrapper around stripe api.
stripe api is very well documented
and
is easy to use.
Since we can’t use stripe.js
we will
use stripe api.
Here, the header has 3 keys. Lets go through them one-by-one:
'Accept': 'application/json' : Designates the content to be in JSON format.
'Content-Type': 'application/x-www-form-urlencoded' : This tells the endpoint that the payload will be one giant query string where name and value pairs will be separated by the ampersand.
'Authorization': 'Bearer ' + '<YOUR-STRIPE-API-KEY>' : This key is to authorize our actions on Stripe. Here Bearer is just a prefix which we need to attach to the api-key because Stripe uses OAuth 2.0 . You can read more on the Bearer token usage here .
Since the Content-Type is application/x-www-form-urlencoded,
the payload should be one query string.
An example of this would be: city=Miami&state=Florida
Let’s prepare a proper payload.
var formBody = [];
for (var property in cardDetails) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(cardDetails[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
That’s it.
Now we can attach formBody to
the body part of the fetch request
and
we are good to go.
Let’s look at by default what kind of response headers we get when we
start with a brand new Rails 4.2.4 application.
Now let’s say that I want to set a custom response header. That’s easy.
All I need to do is add following line of code in the controller.
response.headers['X-Tracking-ID'] = '123456'
Now I see this custom response header.
Setting custom response header for assets
Let’s say that I need that custom response header not only for standard
web requests but also for my assets.
For example application.js file is served by Rails in localhost.
How would I set custom header to the asset being served by Rails here.
Actually that’s not possible in Rails 4. In Rails 4 we can set only one
response header for assets that are served by Rails and that response
header is Cache-Control.
Here is how I can configure Cache-Control for assets.
# open config/environments/production.rb and add following line
config.static_cache_control = 'public, max-age=1000'
Now we have modified ‘Cache-Control` header for the assets.
Besides Cache-Control no other response header can be set for
assets served by Rails.
That’s a limitation.
How Rails Apps lived with this limitation so far
Rails is not the best server to serve static assets.
Apace and NGINX are much better at this job.
Hence ,in reality, in production almost everyone puts either Apache or
NGINX in front of a Rails server and in this way Rails does not have to
serve static files.
Havig said that, Rails applications hosted at Heroku is an exception.
Assets for Rails applications running at Heroku are served by Rails
application itself.
Problem we ran into
Our website is hosted at heroku.
When we ran Google page speed insights
for our website we were warned that we were not using Expires header
for the assets.
Here are how the header looked for application.js.
Now you see the problem we are running into.
Our application is hosted at Heroku.
Heroku lets Rails serve the assets.
Google page speed insights wants us to set Expires header to the assets.
Rails application allows only one header for the assets and that is ‘Cache-Control`.
One easy solution is to host our website at Digital Ocean and then use
Apache or NGINX.
Behind the scenes, Rails uses ActionDispatch::Static middleware to take care of serving assets.
For example, a request to fetch an image, goes through ActionDispatch::Static in the request cycle.
ActionDispatch::Static takes care of serving Rack::File object from server with appropriate headers set in the
response. The served image can have headers like Content-Type, Cache-Control.
Start using Rails master
To fix this, we first pointed the App to use Rails master.
gem'rails',github: 'rails/rails'gem'rack',github: 'rack/rack'# Rails depends on Rack 2gem'arel',github: 'rails/arel'# Rails master works alongside of arel master.
Next, we changed asset configuration to start providing and using missing header for Expires.
Here, we are first setting the Cache-Control header to use public (intermediate) caching,
for a year (31536000 seconds) with max-age and s-maxage.
Here s-maxage stands for Surrogate cache, which is used to cache
internally by Fastly.
We then provide the missing Expires value with some future date in Internet formatted time value.
With this setup, we can see PageSpeed pickups the new headers on assets and does not warn us for the missing header.
Here is the changed response header for asset.
Further Reading
For better use and more details about different headers to use for the assets, please refer to
RFC 2616.
It’s now 5 days since I talked to Apple support team and showed to them
problem I was facing with activating my iTunesConnect account. So far I have not
received any response from Apple.
Devise is doing the right thing by enforcing a unique index on
reset_password_token so that when a user clicks on a link to reset
password the application would know who the user is.
However here is the problem. If we add a new user then by default the
value of reset_password_token is NULL. If we add another user then we
have two records with NULL value in reset_password_token. This works
in PostgreSQL.
But SQL Server would not allow to have two records with NULL in
reset_password_token column.
So how do we solve this problem.
Partial index to rescue.
It is also known as Filtered index.
Both PostgreSQL
and
SQL server support it.
Rails also supports partial index by allowing us to pass where
option as shown below.
add_index:users,:reset_password_token,unique: true,where: 'reset_password_token IS NOT NULL'
Please visit this issue
if you want to see detailed discussion on this topic.
This behavior of SQL Server comes in play in various forms.
Let’s say that we are adding api_auth_token to an existing users
table.
Typically a migration for that might look like as shown below.
In this case we have plenty of records in the users table so the above
migration will fail in PostgreSQL. We will have to resort to usage
of partial index to fix this issue.
Adding not null constraint on a column with an index
In SQL Server
a “not null constraint” cannot be added
on a column which has a index on
it.
We need to first remove the unique index, then add the “not null” constraint and then add the unique index back.
The other solution is to add not NULL constraint first in the
migration and then add any index.
Serialize array into a string column
ActiveRecord supports Array Datatype
for PostgreSQL. We were using this feature to store a list of IDs.
After switching to SQL server we converted the column into string type
and serialized the array.
Pycharm
is a convenient IDE to work with
Robot framework.
To run a test suite or a test script,
one can do so only through console.
Running tests through console is very demanding.
If user can run tests from Pycharm itself then that helps improve productivity.
This blog explains how to configure Pycharm to be able to run test suite
or a single test from the IDE itself.
"use strict";vara=10;varb=42;if(true){console.log("a is less than b");}else{console.log("b is less than a");}
If condition changed from (a < b) to (true).
The constant-folding plugin has smartly evaluated the conditional expression a < b and
replaced the code with the result of that expression.
This plugin can also optimize other expressions as shown below.
// Unary operatorsconsole.log(!true);// Binary expressionconsole.log(20+22);// Function callsconsole.log(Math.min(91,2+20));
Which gets optimized to:
// Unary operatorsconsole.log(false);// Binary expressionconsole.log(42);// Function callsconsole.log(22);
How does this actually work?
Though we are using a “constant folding plugin”
to achieve this optimization, the real work happens in Babel itself.
For every expression in the code, the constant folding plugin calls evaluate function from Babel source code.
This function checks whether it can confidently find end value for a given expression.
The evaluate function returns confidence level and end value for any given expression. Based on this “confidence level”, constant folding plugin replaces the expression with their end values altering our original code as follows.
How evaluate handles different cases
For code Math.min(10, 20), evaluate will return
{ confident: true, value: 10 }
For code a < b, evaluate will return
{ confident: true, value: true }.
But for user defined function like foo('bar') or browser defined console.log('hello'), evaluate will return
{ confident: false, value: undefined }.
In the above case “confident” value will be “false” even if function returns a constant value. For example
for code foo(100), evaluate will return
{ confident: false, value: undefined }.
In the above case function foo will always return 100. Still Babel has low confidence level. Why? That’s because
Babel sees that it is a function and it bails out. It does not even look inside to try to figure things out.
Here is
evaluate code in Babel. You should check it out.
How much optimization is possible?
How much help we will get from Babel for optimizing our code? Will it optimize everything?
The answer is unfortunately no.
As of now, Babel optimizes logical, binary, conditional expressions. It can also evaluate function calls on literals like "babel".length confidently if the literal is string or number.
For function calls, it supports only certain callees like String, Number and Math. So call to a user defined function, even if it’s returning a fixed value, will not be optimized.
Experimental feature
This feature looks great. But it’s available as experimental feature.
If you use the plugin you will get
following warning. unless you enable experimental flag.
$ babel --plugins constant-folding index.js --out-file bundle.js -w
[BABEL] index.js: THE TRANSFORMER constant-folding HAS BEEN MARKED AS EXPERIMENTAL AND IS WIP. USE AT YOUR OWN RISK. THIS WILL HIGHLY LIKELY BREAK YOUR CODE SO USE WITH **EXTREME** CAUTION. ENABLE THE `experimental` OPTION TO IGNORE THIS WARNING.
In order to get rid of warning you need to pass --experimental flag
like this.
In above code example, we know that the result of if (a < b) is true based on values of a and b.
Since the result is not going to change no matter what
there is no need
to have the if and else clauses.
That’s dead code.
Can Babel help us eliminate dead code?
Yes with the help of minification.deadCodeElimination option.
I have been developing a new iOS app using React Native.
I have been
testing it using simulator provided by Xcode. Now it’s time to test the
app using a real device.
One could ask why test on a real device if it works perfectly on a simulator.
Here are some of the reasons.
There are a number of device specific features which are not testable on a simulator. Phone calls, camera, gps data, compass and vibration are a few of them.
Testing unexpected cases that can only be tested on a real device. Like how your application handles incoming calls, low memory situations, low disk space, limited data connectivity etc.
Hardware-wise, your device is different than your Mac. If your app is graphics intensive and requires a lot of CPU usage, it might work seamlessly on your simulator depending on your Mac’s specifications. But it might be laggy and glitchy on the real device.
Now let’s look into how you can start testing your React Native app on an real iPhone.
Step 1 - Plug in your device
Plug in your device to your Mac and open Xcode.
You will be able to select your device to the right of the Play and Stop buttons.
You might run into one of the following two errors if you have not enrolled into Apple’s Developer Program.
Error 1 : Failed to code sign
To fix above issues please enroll in Apple Developer program.
Error 2 : Disk Image could not be mounted
The Developer Disk Image could not be mounted :
You may be running a version of iOS that is not supported by this version of XCode
This can happen if your iOS version is not supported by your current version of Xcode.
To fix this, just update your Xcode to the latest version from the App Store.
Step 2 - Set right deployment target
In your Xcode project setup the Deployment Target should be set to less than or equal to the iOS version installed on the device.
If your iPhone is running iOS 7.0 and you have set “Deployment Target” as 8.0 then the app will not work.
If your iPhone is running iOS 8.0 and you have set “Deployment Target” as 7.0 then the app will work.
Step 3 - Fix “Could not connect to development server” error
So the app is installed and you can see the launch screen.
That’s great.
But soon you might get following error on your iPhone.
Could not connect to development server.
Ensure node server is running and available on the same
network – run ‘npm start’ from react-native root.
Save and click run again. This will fix the error.
Step 3 - Fix connecting to API hosted on local development server
Now the app is installed and you can navigate through the app screens.
If the app attempts to make API call to the server running
locally on the machine then depending on how the local server was
started it could be a problem.
In my case, I have a Ruby on Rails application running on my local machine.
I had started my rails server by executing command rails server.
To do this, the app should be able to access the rails server using the private ip of the server e.g. 192.168.x.x:3000.
Turned out that I had started my rails server using command rails server.
Becase of
change in Rails 4.2
rails server listens to localhost and not 0.0.0.0.
The rails guide further says following.
with this change you will no longer be able to access the Rails server from a different machine, for example if your development environment is in a virtual machine and you would like to access it from the host machine. In such cases, please start the server with rails server -b 0.0.0.0 to restore the old behavior.
In this case iPhone is trying to talk to Rails server so Rails server
must be started using following command.
rails server -b 0.0.0.0
or
rails server --binding=0.0.0.0
By doing so, now you can connect to your Rails app from your local network, by browsing to http://192.168.x.x:3000.
Summary
These were some of the issues I came across while testing my iOS app on an actual device and how I fixed them.
Hopefully, this blog post helps you fix these error cases get you started on testing quickly.
Action Cable is still under
heavy development but it comes with examples
to demonstrate its usage.
Action Cable integrates websocket based real-time communication in Ruby on Rails applications.
It allows building realtime applications like Chats, Status updates, etc.
Action Cable + React
Action Cable provides real time communication. ReactJS is a good tool to
manage view complexity on the client side. Together they make it easy to
develop snappy web applications which requires state management on
the client side without too much work.
Anytime data changes the new data is instantly provided by Action Cable
and the new data is shown on the view without user doing anything on the
application by ReactJS.
Integrating React
The official Action Cable Example is a chat application. We will be
building the same application using ReactJS.
First follow the instructions mentioned to get a working chat application using Action Cable.
Now that the chat application is working let’s get started with adding
ReactJS to the application.
Please note that we have also posted a number of
videos
on learning
ReactJS. Check them out if you are interested.
Step 1 - Add required gems to Gemfile
# react-rails isn't compatible yet with latest Sprockets.# https://github.com/reactjs/react-rails/pull/322gem'react-rails',github: 'vipulnsward/react-rails',branch: 'sprockets-3-compat'# Add support to use es6 based on top of babel, instead of using coffeescriptgem'sprockets-es6'
Step 3 - Setup Action Cable to start listening to events
We will be using es6, so lets replace the file app/assets/javascripts/channels/index.coffee, with app/assets/javascripts/channels/index.es6 and add following code.
Also remove file app/assets/javascripts/channels/comments.coffee, which is used to setup subscription. We will be doing this setup from our React Component.
Step 4 - Create CommentList React Component
Add following code to app/assets/javascripts/components/comments_list.js.jsx.
This would push JSON data to our CommentList component.
Step 6 - Create Rails Views to display component
We now need to setup our views for Message and display of Comments.
We need form to create new Comments on messages. This already exists in app/views/comments/_new.html.erb and we will use it as is.
<%= form_for [ message, Comment.new ], remote: true do |form| %>
<%=form.text_area:content,size: '100x20'%><br><%= form.submit 'Post comment' %>
<% end %>
After creating comment we need to replace current form with new form, following view takes care of that.
From the file app/views/comments/create.js.erbdelete the line
containing following code. Please note that below line needs to be
deleted.
$('#comments').append('<%=j render @comment %>');
We need to display the message details and render our component to display comments.
Insert following code in app/views/messages/show.html.erb just before <%= render 'comments/comments', message: @message %>
In our React Component, we use App.cable.subscriptions.create to create a new subscription for updates, and pass the
channel we want to listen to. It accepts following methods for callback
hooks.
connected: Subscription was connected successfully. Here we use perform method to call related action,
and pass data to the method. perform('follow', {message_id: this.message_id}), 1000), calls CommentsChannel#follow(data).
received: We received new data notification from Rails. Here we take action to update our Component.
We have passed updateCommentList: this.updateCommentList, which is a Component method that is called with data received from Rails.
Complete React Component
Here’s how our complete Component looks like.
varCommentList=React.createClass({getInitialState(){letmessage=JSON.parse(this.props.message);return{message:message};},render(){letcomments=this.state.message.comments.map((comment)=>{returnthis.renderComment(comment);});return(<div>{comments}</div>
);},renderComment(comment){return(<articlekey={comment.id}><h3>Commentby{comment.user.name}</h3>
<p>{comment.content}</p>
</article>
);},componentDidMount(){this.setupSubscription();},updateCommentList(comment){letmessage=JSON.parse(comment);this.setState({message:message});},setupSubscription(){App.comments=App.cable.subscriptions.create("CommentsChannel",{message_id:this.state.message.id,connected:function(){// Timeout here is needed to make sure Subscription// is setup properly, before we do any actions.setTimeout(()=>this.perform('follow',{message_id:this.message_id}),1000);},received:function(data){this.updateCommentList(data.comment);},updateCommentList:this.updateCommentList});}});
Step 7 - Broadcast message when a new comment is created.
Our final piece is to broadcast new updates to message to the listeners, that have subscribed to the channel.
Add following code to app/jobs/message_relay_job.rb
We are using message relay here, and will be getting rid of existing comment relay file - app/jobs/comment_relay_job.rb.
We will also remove reference to CommentRelayJob from Comment model, since after_commit it now calls the MessageRelayJob.
Summary
Hopefully we have shown that Action Cable is going to be a good friend
of ReactJS in future. Only time will tell.
Complete working example for Action Cable + ReactJS can be found here.
Let’s say that we have a Service that reads and writes to Redis.
We have BaseRedisService for managing connection,
RedisWriterService to write to Redis and
RedisReaderService to read from Redis.
require'redis'# Base class to manage connectionclassBaseRedisServiceREDIS_HOST='127.0.0.1'defconnectionif!(defined?(@@connection)&&@@connection)@@connection=Redis.new({host: REDIS_HOST})end@@connectionendend
# Class to write to RedisclassRedisWriterService<BaseRedisServiceattr_reader:key,:valuedefinitializekey,value@key,@value=key,valueenddefprocessconnection.setkey,valueendend
# Class to read from RedisclassRedisReaderService<BaseRedisServiceattr_reader:keydefinitializekey@key=keyenddefprocessconnection.getkeyendend
A test for the above mentioned case might look like this.
require'test_helper'classRedisPubSubServiceTest<ActiveSupport::TestCasedeftest_writing_and_reading_value_using_redisvalue='Vipul A M'RedisWriterService.new('name',value).processassert_equalvalue,RedisReaderService.new('name').processendend
But now, we need to add PubSub to the service and verify that
the service sends proper messages to Redis.
For verifying from such a service, the service would need to listen to messages sent to Redis.
Problem is that Redis listens in a loop. We would need to explicitly release control from listening and
allow our tests to go ahead and verify some scenario.
Notice that we don’t have control over returning value from the loop easily. Right now we just print on receiving a new message.
Now, lets start persisting our messages to some array in our Service.
Also we will start exposing this from a thread variable so that it could be accessed from outside of execution
of this listen loop.
We now have a way to access message state from the service to read any messages received by it.
Lets say we define a new SubscriberService from a Thread, we could read messages like this.
subscriber=Thread.new{RedisSubscriberService.new('payment_alerts').process}# Print first message from messages receivedputssubscriber[:messages].first
Armed with this, we can now define a set of Rails helpers to use in our Rails tests.
Notice the with_subscriber method. It executes some code passed to it, then passes method
execution to the subscriber process to read any messages sent and store onto messages store.
The count of the variable THREAD_PROCESS_TIMEOUT, can be experimented to set to a value
that suites the system that’s being verified.
In our tests, we can now verify PubSub as-
require'test_helper'classRedisPubSubServiceTest<ActiveSupport::TestCaseincludeSubscriberHelpersdefsetupsetup_subscriberenddefteardownteardown_subscriberenddeftest_writing_and_reading_back_values_from_pub_subvalue='Vipul A M'with_subscriberdoRedisPublisherService.new('test_channel',value).processendassert_equalvalue,message_from_subscriptionendend
Summary
We took a look at how PubSub based services can be verified by using threads and exposing
messages from them, for verification. These can be tailored to support any similar PubSub
service like Redis, and can be used to easily verify values being published from our services
from Rails tests.
In my previous blog post,
I talked about how to do phone verification using SMS and now in this blog post I’m going to talk
about how to do voice based phone verification using Twilio.
Requirement
Let’s change the requirement a bit this time.
After the user signs up, send an SMS verification.
If SMS verification goes well then use that phone number for future use.
But if the user’s phone number doesn’t support the SMS feature, then call on user’s
phone number for the verification.
In the call, ask the user to press 1 from keypad to complete the verification.
If that user presses the 1 key, then mark the phone number as verified.
Step 1: Make a call for phone verification
We already handled SMS verification, so let’s add call related changes in
PhoneVerificationService class.
classPhoneVerificationServiceattr_reader:user,:verification_throughVERIFICATION_THROUGH_SMS=:smsVERIFICATION_THROUGH_CALL=:calldefinitializeoptions@user=User.find(options[:user_id])@verification_through=options[:verification_through]||VERIFICATION_THROUGH_SMSenddefprocessifverification_through==VERIFICATION_THROUGH_SMSperform_sms_verificationelsemake_callendendprivatedeffrom#phone number given by twilioSettings.twilio_number_for_appenddefto"+1#{user.phone_number}"enddefbody"Please reply with this code '#{user.phone_verification_code}'"<<"to verify your phone number"enddefsend_smsRails.logger.info"SMS: From: #{from} To: #{to} Body: \"#{body}\""twilio_client.account.messages.create(from: from,to: to,body: body)enddefmake_callRails.logger.info"Call: From: #{from} To: #{to}"twilio_client.account.calls.create(from: from,to: to,url: callback_url)enddefperform_sms_verificationbeginsend_smsrescueTwilio::REST::RequestError=>ereturnmake_callife.message.include?('is not a mobile number')raisee.messageendenddefcallback_urlRails.application.routes.url_helpers.phone_verifications_voice_url(host: Settings.app_host,verification_code: user.phone_verification_code)enddeftwilio_client@twilio||=Twilio::REST::Client.new(Settings.twilio_account_sid,Settings.twilio_auth_token)endend
In PhoneVerificationService class we have added some major changes:
We have defined one more attribute reader verification_through and in initialize method
we have set it.
We have created two new constants VERIFICATION_THROUGH_SMS & VERIFICATION_THROUGH_CALL.
We have changed process method and now we check which verification process should
be taken place based on verification_through attribute.
We also have added new methods perform_sms_verification, make_call and callback_url.
Let’s go through these newly added methods.
perform_sms_verification:
In this method, first we try to send an SMS verification. If SMS verification
fails with error message like ‘is not a mobile number’ then in
the rescue block, we make a phone verification call or we raise the error.
make_call:
In this method, we create an actual phone call and pass the all required info to
twilio client object.
callback_url:
In this method, we set the callback_url which is
required for Twilio for making a call. When we call the user for
verification, our app needs to behave like a human and should ask to the user to
press 1 to complete the verification (i.e. In the form of TwiML).
This callback_url needs be to set in our app.
Step 2: Add voice action in phone verification controller
For callback_url, add route for voice action in config/routes.rb
post 'phone_verifications/voice' => 'phone_verifications#voice'
Add voice action and required code for it in PhoneVerificationsController.
classPhoneVerificationsController<ApplicationControllerskip_before_filter:verify_authenticity_tokenafter_filter:set_headerHUMAN_VOICE='alice'defvoiceverification_code=params[:verification_code]response=Twilio::TwiML::Response.newdo|r|r.GathernumDigits: '1',action: "/phone_verifications/verify_from_voice?verification_code=#{verification_code}",method: 'post'do|g|g.Say'Press 1 to verify your phone number.',voice: HUMAN_VOICEendendrender_twimlresponseenddefverify_from_messageuser=get_user_for_phone_verificationuser.mark_phone_as_verified!ifuserrendernothing: trueendprivatedefget_user_for_phone_verificationphone_verification_code=params['Body'].try(:strip)phone_number=params['From'].gsub('+1','')condition={phone_verification_code: phone_verification_code,phone_number: phone_number}User.unverified_phones.where(condition).firstenddefset_headerresponse.headers["Content-Type"]="text/xml"enddefrender_twiml(response)rendertext: response.textendend
In this voice method, we have set up the Twilio response. When a call is made to
the user, this response will get converted into robotic human voice. render_twiml
method which sets twilio response in
text form is required for Twilio APIs. Set up the response header in the
set_header method which gets called in after_filter method.
Step 3: Set request URL for voice phone verification in Twilio
In voice action method, we have setup the request url in the action key of Twilio
response object, that also needs to be set in your Twilio account. So when, user replies
back to the call query, Twilio will make a request to our app and adds its own some values
as parameters to the request. Using those parameters we handle the actual phone
verification in the app.
Open twilio account and under NUMBERS section/tab, click on your Twilio number.
Then in Voice section, add request URL with HTTP POST method. Add URL like this.
We need Ngrok to expose local url to external world.
Read more about it in my previous blog post.
Step 4: Add verify_from_voice action in phone verification controller
First add route for this action in config/routes.rb
post "phone_verifications/verify_from_voice" => "phone_verifications#verify_from_voice
Add this method, in your PhoneVerificationsController.
defverify_from_voiceresponse=Twilio::TwiML::Response.newdo|r|ifparams['Digits']=="1"user=get_user_for_phone_verificationuser.mark_phone_as_verified!r.Say'Thank you. Your phone number has been verified successfully.',voice: HUMAN_VOICEelser.Say'Sorry. Your phone number has not verified.',voice: HUMAN_VOICEendendrender_twimlresponseend
Modify private method get_user_for_phone_verification to support voice verification
in PhoneVerificationsController.
In the verify_from_voice method, we get parameters Digits, To & verification_code
from Twilio request. Using these parameters, we search for user in the database. If we get the
proper user then we mark user’s phone number as verified phone number.
Next time you are using an after_commit callback and a transaction,
make sure that code inside the transaction is not dependent on the
result of the callback.