Action Mailer allows you to send email from your application using a mailer model and views.
To use Action Mailer, you need to create a mailer model.
$ rails generate mailer Notifier
The generated model inherits from ActionMailer::Base
. Emails
are defined by creating methods within the model which are then used to set
variables to be used in the mail template, to change options on the mail,
or to add attachments.
Examples:
class Notifier < ActionMailer::Base default :from =><EMAIL_ADDRESS>:return_path =><EMAIL_ADDRESS>def welcome(recipient) @account = recipient mail(:to => recipient.email_address_with_name, :bcc =><EMAIL_ADDRESS>"Order Watcher<EMAIL_ADDRESS>end end
Within the mailer method, you have access to the following methods:
attachments[]=
- Allows you to add attachments to your email
in an intuitive manner; attachments['filename.png'] =
File.read('path/to/filename.png')
attachments.inline[]=
- Allows you to add an inline attachment
to your email in the same manner as attachments[]=
headers[]=
- Allows you to specify any header field in your
email such as headers['X-No-Spam'] = 'True'
. Note, while most
fields like To:
From:
can only appear once in an
email header, other fields like X-Anything
can appear multiple
times. If you want to change a field that can appear multiple times, you
need to set it to nil first so that Mail knows you are replacing it and not
adding another field of the same name.
headers(hash)
- Allows you to specify multiple headers in your
email such as headers({'X-No-Spam' => 'True', 'In-Reply-To' =>
<EMAIL_ADDRESS>mail
- Allows you to specify email to be sent.
The hash passed to the mail method allows you to specify any header that a Mail::Message will accept (any valid Email header including optional fields).
The mail method, if not passed a block, will inspect your views and send
all the views with the same name as the method, so the above action would
send the welcome.text.erb
view file as well as the
welcome.text.html.erb
view file in a
multipart/alternative
email.
If you want to explicitly render only certain templates, pass a block:
mail(:to => user.email) do |format| format.text format.html end
The block syntax is also useful in providing information specific to a part:
mail(:to => user.email) do |format| format.text(:content_transfer_encoding => "base64") format.html end
Or even to render a special view:
mail(:to => user.email) do |format| format.text format.html { render "some_other_template" } end
Like Action Controller, each mailer class has a corresponding view directory in which each method of the class looks for a template with its name.
To define a template to be used with a mailing, create an .erb
file with the same name as the method in your mailer model. For example, in
the mailer defined above, the template at
app/views/notifier/welcome.text.erb
would be used to generate
the email.
Variables defined in the model are accessible as instance variables in the view.
Emails by default are sent in plain text, so a sample view for our model example might look like this:
Hi <%= @account.name %>, Thanks for joining our service! Please check back often.
You can even use Action Pack helpers in these views. For example:
You got a new note! <%Q<EMAIL_ADDRESS>:length => 25) %>
If you need to access the subject, from or the recipients in the view, you can do that through message object:
You got a new note from <%Q message.from %>! <%=<EMAIL_ADDRESS>:length => 25) %>
URLs can be generated in mailer views using url_for
or named
routes. Unlike controllers from Action Pack, the mailer instance doesn’t
have any context about the incoming request, so you’ll need to provide all
of the details needed to generate a URL.
When using url_for
you’ll need to provide the
:host
, :controller
, and :action
:
<%Q url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
When using named routes you only need to supply the :host
:
<%Q users_url(:host => "example.com") %>
You should use the named_route_url
style (which generates
absolute URLs) and avoid using the named_route_path
style
(which generates relative URLs), since clients reading the mail will have
no concept of a current URL from which to determine a relative path.
It is also possible to set a default host that will be used in all mailers
by setting the :host
option as a configuration option in
config/application.rb
:
config.action_mailer.default_url_options = { :host => "example.com" }
Setting ActionMailer::Base.default_url_options
directly is now
deprecated, use the configuration option mentioned above to set the default
host.
If you do decide to set a default :host
for your mailers you
want to use the :only_path => false
option when using
url_for
. This will ensure that absolute URLs are generated
because the url_for
view helper will, by default, generate
relative URLs when a :host
option isn't explicitly provided.
Once a mailer action and template are defined, you can deliver your message or create it and save it for delivery later:
Notifier.welcome(david).deliver # sends the email mail = Notifier.welcome(david) # => a Mail::Message object mail.deliver # sends the email
You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
Multipart messages can also be used implicitly because Action Mailer will automatically detect and use multipart templates, where each template is named after the name of the action, followed by the content type. Each such detected template will be added as a separate part to the message.
For example, if the following templates exist:
signup_notification.text.erb
signup_notification.text.html.erb
signup_notification.text.xml.builder
signup_notification.text.yaml.erb
Each would be rendered and added as a separate part to the message, with
the corresponding content type. The content type for the entire message is
automatically set to multipart/alternative
, which indicates
that the email contains multiple different representations of the same
email body. The same instance variables defined in the action are passed to
all email templates.
Implicit template rendering is not performed if any attachments or parts
have been added to the email. This means that you’ll have to manually add
each part to the email and set the content type of the email to
multipart/alternative
.
Sending attachment in emails is easy:
class ApplicationMailer < ActionMailer::Base def welcome(recipient) attachments['free_book.pdf'] = File.read('path/to/file.pdf') mail(:to => recipient, :subject => "New account information") end end
Which will (if it had both a welcome.text.erb
and
welcome.text.html.erb
template in the view directory), send a
complete multipart/mixed
email with two parts, the first part
being a multipart/alternative
with the text and HTML email parts inside, and the second being a
application/pdf
with a Base64 encoded copy of the file.pdf
book with the filename free_book.pdf
.
You can also specify that a file should be displayed inline with other HTML. This is useful if you want to display a corporate logo or a photo.
class ApplicationMailer < ActionMailer::Base def welcome(recipient) attachments.inline['photo.png'] = File.read('path/to/photo.png') mail(:to => recipient, :subject => "Here is what we look like") end end
And then to reference the image in the view, you create a
welcome.html.erb
file and make a call to
image_tag
passing in the attachment you want to display and
then call url
on the attachment to get the relative content id
path for the image source:

Please Don't Cringe
 <%= image_tag attachments['photo.png'].url -%>
As we are using Action View’s image_tag
method, you can pass
in any other options you want:

Please Don't Cringe
 <%= image_tag attachments['photo.png'].url, :alt => 'Our Photo', :class => 'photo' -%>
Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to register objects that are called during the mail delivery life cycle.
An observer object must implement the
:delivered_email(message)
method which will be called once for
every email sent after the email has been sent.
An interceptor object must implement the
:delivering_email(message)
method which will be called before
the email is sent, allowing you to make modifications to the email before
it hits the delivery agents. Your object should make any needed
modifications directly to the passed in Mail::Message instance.
Action Mailer provides some intelligent defaults for your emails, these are usually specified in a default method inside the class definition:
class Notifier < ActionMailer::Base default :sender =><EMAIL_ADDRESS>end
You can pass in any header value that a Mail::Message
accepts.
Out of the box, ActionMailer::Base
sets the following:
:mime_version => "1.0"
:charset => "UTF-8",
:content_type => "text/plain",
:parts_order => [ "text/plain",
"text/enriched", "text/html" ]
parts_order
and charset
are not actually valid
Mail::Message
header fields, but Action Mailer translates them
appropriately and sets the correct values.
As you can pass in any header, you need to either quote the header as a string, or pass it in as an underscored symbol, so the following will work:
class Notifier < ActionMailer::Base default 'Content-Transfer-Encoding' => '7bit', :content_description => 'This is a description' end
Finally, Action Mailer also supports passing Proc
objects into
the default hash, so you can define methods that evaluate as the message is
being generated:
class Notifier < ActionMailer::Base default 'X-Special-Header' => Proc.new { my_method } private def my_method 'some complex call' end end
Note that the proc is evaluated right at the start of the mail message generation, so if you set something in the defaults using a proc, and then set the same thing inside of your mailer method, it will get over written by the mailer method.
These options are specified on the class level, like
ActionMailer::Base.raise_delivery_errors = true
default
- You can pass this in at a class level as well as
within the class itself as per the above section.
logger
- the logger is used for generating information on the
mailing run if available. Can be set to nil for no logging. Compatible with
both Ruby's own Logger and Log4r loggers.
smtp_settings
- Allows detailed configuration for
:smtp
delivery method:
:address
- Allows you to use a remote mail server. Just change
it from its default "localhost" setting.
:port
- On the off chance that your mail server doesn't run on
port 25, you can change it.
:domain
- If you need to specify a HELO domain, you can do it
here.
:user_name
- If your mail server requires authentication, set
the username in this setting.
:password
- If your mail server requires authentication, set
the password in this setting.
:authentication
- If your mail server requires authentication,
you need to specify the authentication type here. This is a symbol and one
of :plain
(will send the password in the clear),
:login
(will send password Base64 encoded) or
:cram_md5
(combines a Challenge/Response mechanism to exchange
information and a cryptographic Message Digest 5 algorithm to hash
important information)
:enable_starttls_auto
- When set to true, detects if STARTTLS
is enabled in your SMTP server and starts to use it.
:openssl_verify_mode
- When using TLS, you can set how OpenSSL
checks the certificate. This is really useful if you need to validate a
self-signed and/or a wildcard certificate. You can use the name of an
OpenSSL verify constant ('none', 'peer',
'client_once','fail_if_no_peer_cert') or directly the constant
(OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...).
sendmail_settings
- Allows you to override options for the
:sendmail
delivery method.
:location
- The location of the sendmail executable. Defaults
to /usr/sbin/sendmail
.
:arguments
- The command line arguments. Defaults to -i
-t
with -f sender@address
added automatically before
the message is sent.
file_settings
- Allows you to override options for the
:file
delivery method.
:location
- The directory into which emails will be written.
Defaults to the application tmp/mails
.
raise_delivery_errors
- Whether or not errors should be raised
if the email fails to be delivered.
delivery_method
- Defines a delivery method. Possible values
are :smtp
(default), :sendmail
,
:test
, and :file
. Or you may provide a custom
delivery method object eg. MyOwnDeliveryMethodClass.new. See the Mail gem
documentation on the interface you need to implement for a custom delivery
agent.
perform_deliveries
- Determines whether emails are actually
sent from Action Mailer when you call .deliver
on an mail
message or on an Action Mailer method. This is on by default but can be
turned off to aid in functional testing.
deliveries
- Keeps an array of all the emails sent out through
the Action Mailer with delivery_method :test
. Most useful for
unit and functional testing.
default_charset
- This is now deprecated, use the
default
method above to set the default :charset
.
default_content_type
- This is now deprecated, use the
default
method above to set the default
:content_type
.
default_mime_version
- This is now deprecated, use the
default
method above to set the default
:mime_version
.
default_implicit_parts_order
- This is now deprecated, use the
default
method above to set the default
:parts_order
. Parts Order is used when a message is built
implicitly (i.e. multiple parts are assembled from templates which specify
the content type in their filenames) this variable controls how the parts
are ordered.
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 401 def default(value = nil) self.default_params = default_params.merge(value).freeze if value default_params end
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 395 def mailer_name @mailer_name ||= name.underscore end
Instantiate a new mailer object. If method_name
is not
nil
, the mailer will be initialized according to the named
method. If not, the mailer will remain uninitialized (useful when you only
need to invoke the “receive” method, for instance).
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 466 def initialize(method_name=nil, *args) super() @_message = Mail.new process(method_name, *args) if method_name end
Receives a raw email, parses it into an email object, decodes it,
instantiates a new mailer, and passes the email object to the mailer
object’s receive
method. If you want your mailer to be able to
process incoming messages, you’ll need to implement a receive
method that accepts the raw email string as a parameter:
class MyMailer < ActionMailer::Base def receive(mail) ... end end
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 417 def receive(raw_mail) ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload| mail = Mail.new(raw_mail) set_payload_for_mail(payload, mail) new.receive(mail) end end
Register an Interceptor which will be called before mail is sent. Either a
class or a string can be passed in as the Interceptor. If a string is
passed in it will be constantize
d.
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 390 def register_interceptor(interceptor) delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor) Mail.register_interceptor(delivery_interceptor) end
Register one or more Interceptors which will be called before mail is sent.
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 375 def register_interceptors(*interceptors) interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) } end
Register an Observer which will be notified when mail is delivered. Either
a class or a string can be passed in as the Observer. If a string is passed
in it will be constantize
d.
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 382 def register_observer(observer) delivery_observer = (observer.is_a?(String) ? observer.constantize : observer) Mail.register_observer(delivery_observer) end
Register one or more Observers which will be notified when mail is delivered.
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 370 def register_observers(*observers) observers.flatten.compact.each { |observer| register_observer(observer) } end
Allows you to add attachments to an email, like so:
mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
If you do this, then Mail will take the file name and work out the mime type set the Content-Type, Content-Disposition, Content-Transfer-Encoding and base64 encode the contents of the attachment all for you.
You can also specify overrides if you want by passing a hash instead of a string:
mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip', :content => File.read('/path/to/filename.jpg')}
If you want to use a different encoding than Base64, you can pass an encoding in, but then it is up to you to pass in the content pre-encoded, and don’t expect Mail to know how to decode this data:
file_content = SpecialEncode(File.read('/path/to/filename.jpg')) mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip', :encoding => 'SpecialEncoding', :content => file_content }
You can also search for specific attachments:
# By Filename mail.attachments['filename.jpg'] # => Mail::Part object or nil # or by index mail.attachments[0] # => Mail::Part (first attachment)
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 533 def attachments @_message.attachments end
Allows you to pass random and unusual headers to the new +Mail::Message+ object which will add them to itself.
headers['X-Special-Domain-Specific-Header'] = "SecretValue"
You can also pass a hash into headers of header field names and values, which will then be set on the Mail::Message object:
headers 'X-Special-Domain-Specific-Header' => "SecretValue", 'In-Reply-To' => incoming.message_id
The resulting Mail::Message will have the following in it’s header:
X-Special-Domain-Specific-Header: SecretValue
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 495 def headers(args=nil) if args @_message.headers(args) else @_message end end
The main method that creates the message and renders the email templates. There are two ways to call this method, with a block, or without a block.
Both methods accept a headers hash. This hash allows you to specify the most used headers in an email message, these are:
:subject
- The subject of the message, if this is omitted,
Action Mailer will ask the Rails I18n class for a translated :subject
in the scope of [:actionmailer, mailer_scope, action_name]
or
if this is missing, will translate the humanized version of the
action_name
:to
- Who the message is destined for, can be a string of
addresses, or an array of addresses.
:from
- Who the message is from
:cc
- Who you would like to Carbon-Copy on this email, can be
a string of addresses, or an array of addresses.
:bcc
- Who you would like to Blind-Carbon-Copy on this email,
can be a string of addresses, or an array of addresses.
:reply_to
- Who to set the Reply-To header of the email to.
:date
- The date to say the email was sent on.
You can set default values for any of the above headers (except :date) by
using the default
class method:
class Notifier < ActionMailer::Base self.default :from =><EMAIL_ADDRESS>:bcc =><EMAIL_ADDRESS>:reply_to =><EMAIL_ADDRESS>end
If you need other headers not listed above, you can either pass them in as
part of the headers hash or use the headers['name'] = value
method.
When a :return_path
is specified as header, that value will be
used as the ‘envelope from’ address for the Mail message. Setting this is
useful when you want delivery notifications sent to a different address
than the one in :from
. Mail will actually use the
:return_path
in preference to the :sender
in
preference to the :from
field for the ‘envelope from’ value.
If you do not pass a block to the mail
method, it will find
all templates in the view paths using by default the mailer name and the
method name that it is being called from, it will then create parts for
each of these templates intelligently, making educated guesses on correct
content type and sequence, and return a fully prepared Mail::Message ready
to call :deliver
on to send.
For example:
class Notifier < ActionMailer::Base default :from =><EMAIL_ADDRESS>def welcome mail(:to =><EMAIL_ADDRESS>end end
Will look for all templates at “app/views/notifier” with name “welcome”. However, those can be customized:
mail(:template_path => 'notifications', :template_name => 'another')
And now it will look for all templates at “app/views/notifications” with name “another”.
If you do pass a block, you can render specific templates of your choice:
mail(:to =><EMAIL_ADDRESS>do |format| format.text format.html end
You can even render text directly without using a template:
mail(:to =><EMAIL_ADDRESS>do |format| format.text { render :text => "Hello Mikel!" } format.html { render :text => "
Hello Mikel!
" } end
Which will render a multipart/alternative
email with
text/plain
and text/html
parts.
The block syntax also allows you to customize the part headers if desired:
mail(:to =><EMAIL_ADDRESS>do |format| format.text(:content_transfer_encoding => "base64") format.html end
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 623 def mail(headers={}, &block) # Guard flag to prevent both the old and the new API from firing # Should be removed when old API is removed @mail_was_called = true m = @_message # At the beginning, do not consider class default for parts order neither content_type content_type = headers[:content_type] parts_order = headers[:parts_order] # Call all the procs (if any) default_values = self.class.default.merge(self.class.default) do |k,v| v.respond_to?(:call) ? v.bind(self).call : v end # Handle defaults headers = headers.reverse_merge(default_values) headers[:subject] ||= default_i18n_subject # Apply charset at the beginning so all fields are properly quoted m.charset = charset = headers[:charset] # Set configure delivery behavior wrap_delivery_behavior!(headers.delete(:delivery_method)) # Assign all headers except parts_order, content_type and body assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path) assignable.each { |k, v| m[k] = v } # Render the templates and blocks responses, explicit_order = collect_responses_and_parts_order(headers, &block) create_parts_from_responses(m, responses) # Setup content type, reapply charset and handle parts order m.content_type = set_content_type(m, content_type, headers[:content_type]) m.charset = charset if m.multipart? parts_order ||= explicit_order || headers[:parts_order] m.body.set_sort_order(parts_order) m.body.sort_parts! end m end
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 477 def mailer_name self.class.mailer_name end
# File c:/Ruby192/lib/ruby/gems/1.9.1/gems/actionmailer-3.1.1/lib/action_mailer/base.rb, line 671 def set_content_type(m, user_content_type, class_default) params = m.content_type_parameters || {} case when user_content_type.present? user_content_type when m.has_attachments? if m.attachments.detect { |a| a.inline? } ["multipart", "related", params] else ["multipart", "mixed", params] end when m.multipart? ["multipart", "alternative", params] else m.content_type || class_default end end


Hash
Parses a Cache-Control header and exposes the directives as a Hash. Directives that do not have values are set to true.
The expiration time of an entity MAY be specified by the origin server using the Expires header (see section 14.21). Alternatively, it MAY be specified using the max-age directive in a response. When the max-age cache-control directive is present in a cached response, the response is stale if its current age is greater than the age value given (in seconds) at the time of a new request for that resource. The max-age directive on a response implies that the response is cacheable (i.e., “public”) unless some other, more restrictive cache directive is also present.
If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive. This rule allows an origin server to provide, for a given response, a longer expiration time to an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be useful if certain HTTP/1.0 caches improperly calculate ages or expiration times, perhaps due to desynchronized clocks.
Many HTTP/1.0 cache implementations will treat an Expires value that is less than or equal to the response Date value as being equivalent to the Cache-Control response directive “no-cache”. If an HTTP/1.1 cache receives such a response, and the response does not include a Cache-Control header field, it SHOULD consider the response to be non-cacheable in order to retain compatibility with HTTP/1.0 servers.
When the max-age directive is included in the request, it indicates that the client is willing to accept a response whose age is no greater than the specified time in seconds.
# File lib/rack/cache/cachecontrol.rb, line 110 def max_age self['max-age'].to_i if key?('max-age') end
Because a cache MAY be configured to ignore a server’s specified expiration time, and because a client request MAY include a max- stale directive (which has a similar effect), the protocol also includes a mechanism for the origin server to require revalidation of a cache entry on any subsequent use. When the must-revalidate directive is present in a response received by a cache, that cache MUST NOT use the entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server. (I.e., the cache MUST do an end-to-end revalidation every time, if, based solely on the origin server’s Expires or max-age value, the cached response is stale.)
The must-revalidate directive is necessary to support reliable operation for certain protocol features. In all circumstances an HTTP/1.1 cache MUST obey the must-revalidate directive; in particular, if the cache cannot reach the origin server for any reason, it MUST generate a 504 (Gateway Timeout) response.
Servers SHOULD send the must-revalidate directive if and only if failure to revalidate a request on the entity could result in incorrect operation, such as a silently unexecuted financial transaction. Recipients MUST NOT take any automated action that violates this directive, and MUST NOT automatically provide an unvalidated copy of the entity if revalidation fails.
# File lib/rack/cache/cachecontrol.rb, line 152 def must_revalidate? self['must-revalidate'] end
When set in a response, a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent caching even by caches that have been configured to return stale responses to client requests.
Note that this does not necessary imply that the response may not be stored by the cache, only that the cache cannot serve it without first making a conditional GET request with the origin server.
When set in a request, the server MUST NOT use a cached copy for its response. This has quite different semantics compared to the no-cache directive on responses. When the client specifies no-cache, it causes an end-to-end reload, forcing each cache to update their cached copies.
# File lib/rack/cache/cachecontrol.rb, line 49 def no_cache? self['no-cache'] end
Indicates that the response MUST NOT be stored under any circumstances.
The purpose of the no-store directive is to prevent the inadvertent release or retention of sensitive information (for example, on backup tapes). The no-store directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it. This directive applies to both non- shared and shared caches. “MUST NOT store” in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.
The purpose of this directive is to meet the stated requirements of certain users and service authors who are concerned about accidental releases of information via unanticipated accesses to cache data structures. While the use of this directive might improve privacy in some cases, we caution that it is NOT in any way a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.
# File lib/rack/cache/cachecontrol.rb, line 78 def no_store? self['no-store'] end
Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache. This allows an origin server to state that the specified parts of the response are intended for only one user and are not a valid response for requests by other users. A private (non-shared) cache MAY cache the response.
Note: This usage of the word private only controls where the response may be cached, and cannot ensure the privacy of the message content.
# File lib/rack/cache/cachecontrol.rb, line 32 def private? self['private'] end
The proxy-revalidate directive has the same meaning as the must- revalidate directive, except that it does not apply to non-shared user agent caches. It can be used on a response to an authenticated request to permit the user’s cache to store and later return the response without needing to revalidate it (since it has already been authenticated once by that user), while still requiring proxies that service many users to revalidate each time (in order to make sure that each user has been authenticated). Note that such authenticated responses also need the public cache control directive in order to allow them to be cached at all.
# File lib/rack/cache/cachecontrol.rb, line 166 def proxy_revalidate? self['proxy-revalidate'] end
Indicates that the response MAY be cached by any cache, even if it would normally be non-cacheable or cacheable only within a non- shared cache.
A response may be considered public without this directive if the private directive is not set and the request does not include an Authorization header.
# File lib/rack/cache/cachecontrol.rb, line 18 def public? self['public'] end
Generated with the Darkfish Rdoc Generator 2.


--- layout: page title: Hi, I'm Hongyu! subtitle: A Ph.D Student / Algorithm Engineer / Basketball Enthusiast use-site-title: true bigimg: - "/img/bigimg/accv2022_attendees.jpg" : "ACCV Attendees, Macau (2022)" - "/img/bigimg/accv2022_delicious_food.jpg" : "ACCV Delicious Food, Macau (2022)" - "/img/bigimg/late_graduation2021.jpg" : "Late Graduation Ceremony (2021)" - "/img/bigimg/greatwall2021.jpg" : "Yaoziyu, Great Wall (2021)" - "/img/bigimg/rulun_concert.jpg" : "Concert, Rulun Hall (2020)" - "/img/bigimg/grad2020.png" : "Our Graduation on Cloud (2020)" - "/img/bigimg/daihai.jpg" : "Daihai, Inner Mongolia (2020)" - "/img/bigimg/slide_0.jpg" : "DWBI Graduation (2019)" - "/img/bigimg/slide_1.jpg" : "DWBI Graduation (2018)" - "/img/bigimg/people_hall.jpg" : "People's Great Hall (2017)" - "/img/bigimg/ah_cavs_champs.jpg" : "Cavaliers' Championship (2016)" - "/img/bigimg/com_athlete_slider.jpg" : "Five Members of Cavaliers (2016)" ---


{% extends '_layouts/layout_2-3.html' %} {% block page_title %}Index : {{serviceName}}{% endblock %} {% block pageTitle %}Symptoms and conditions / v1d2{% endblock %} {% block pageIntro %}These is the design work for the Connecting to Services programme{% endblock%} {% block content %}


"Without ambition one starts nothing. Without work one finishes nothing. The prize will not be sent to you. You have to win it."
- Ralph Waldo Emerson
"Strive not to be a success, but to be of value."
- Albert Einstein
Adam Brown
Covina, CA 91723
(909)438-5170
adam.brown0719@gmail.com


{{ define "content" }} {{/* The value of {{ . }} in this template is an array of User objects. */}}
Create new user


Last-minute transit talks
by The Canadian PressLast-minute contract talks have been scheduled for Tuesday with a transit strike facing commuters in Metro Vancouver later this week.
Gavin McGarrigle, Unifor's lead negotiator, says the union is prepared to negotiate until midnight Wednesday and if no agreement is reached, a three-day strike will begin, affecting about 350,000 transit users.
Wages are one of the key issues in the dispute.
Unifor has complained the latest offer isn't comparable to salaries in other major cities.
Translink CEO Kevin Desmond says the last wage offer made to the union is based on market conditions.
McGarrigle says the union is prepared to compromise and its members don't expect to earn the wages paid to workers doing the same jobs in Toronto, but they would like to start narrowing the gap.
He says Unifor's national president, Jerry Dias, will join the negotiations.
The union is threatening to call a strike that would affect bus and sea bus ferry services from Wednesday to Friday.
Desmond said Monday that TransLink is looking for ways to increase access to drop-off points near its SkyTrain lines, which would not be affected by a strike.


Mexico arrests suspects in Mormon massacre
MEXICO CITY: Mexico has arrested multiple suspects in the murder of nine Mormon women and children last week, the security minister said on Monday (Nov 11), without giving further details.
"Suspects have been arrested, but we cannot provide any further information, because the investigation is being handled by the federal and Sonora (state) prosecutors' offices," Security Minister Alfonso Durazo told journalists.
Neither office immediately responded to AFP requests for further details.
The massacre of the three women and six children caused shock in both Mexico and the United States, where their families had dual nationality.
The victims, including twin eight-month-old babies, were killed in a hail of bullets a week ago as they drove on a remote road between the states of Sonora and Chihuahua, in northern Mexico, a lawless region disputed by warring drug cartels.
Mexican officials say a drug cartel called La Linea (The Line) may have mistaken the victims for members of a rival gang.
However, relatives maintain the families were deliberately targeted.
Eight children managed to escape, six of them wounded. One 13-year-old boy helped the younger ones hide, then walked 22 kilometres home to get help.
The case has again cast a spotlight on drug cartel-fueled violence in Mexico and leftist President Andres Manuel Lopez Obrador's struggles to rein it in.
Durazo said 12 different teams are working on the investigation, including one from the US Federal Bureau of Investigation (FBI).
Large groups of US Mormons emigrated to Mexico in the late 19th century, fleeing persecution for their traditions, including polygamy.
Now breakaways from the official Church of Jesus Christ of Latter-day Saints, which banned polygamy in 1891, they have lived in Mexico for generations.


perfect backup solution for developers
If you are system administrator, I know you are professional to write script doing flexible customized backup. But if you are like me as a Rails developer, and you just don’t wanna spend too much time to struggle with tricky scripts for file or DB backup, you mush try my recommend backup solution on *nix system. Back to the topic : easy backup with “backup” and “whenever”.
Yes, Backup and Whenever is the best and easiest solution to manage your backup. They are all Ruby gems which are open sourced on github. Most of all, they are all working well both with Rails and with pure *nix environment.
While using Backup, it’s like a configuration file with easy rule, don’t worry about the rule, I believe you will understand it in seconds. It also owns many common slots to different DB(or S3), you can also config your customized command and script inside its backup.rb file. Whenever is the Ruby way to manage your Cron jobs, It’s pretty cool.
Don’t hesitate to try them out, you will find the inside beauty of backup. Enjoy!


The paper proposes a Competitive training mechanism to train a mixture of independent generative models.
The idea is that this mixture of different models would divide the data distribution amongst themselves and specialize to their respective splits.
The training procedure is related to clustering-based methods.
In causal modeling, a common assumption is that the data is generated by a set of independent mechanisms.
It is not known which mechanism generates which datapoint and recovering the underlying mechanisms can be modeled as learning a structural causal generative model.
The paper assumes that the support of the different generators do not overlap, i.e., the underlying data distribution is factorized into non-overlapping regions.
This data factorization is learned using a set of discriminators.
If there are $k$ generators, $k$ binary partition functions $c_i, … c_k$ are used.
For a given datapoint $x$, if $c_i(x) = 1$ then $c_j(x) = 0$ for all other $j$ and $x$ is assigned to $i^{th}$ generator.
For a fixed partition function $c_j^t$ ($t$ denotes the partition function at time $t$), minimize the sum of f-divergence between the model and the data distribution (that is assigned to it). The loss formulation is an upper bound on the f-divergence of the mixture model.
In the next step, the data points are re-assigned to the generative models, based on the likelihood of each data point for each model.
The likelihood is estimated by training a discriminator that can distinguish the generated samples from the real samples.
The independence assumption may be too restrictive because the low-level features will be common across the distribution splits.
This “violation” can be avoided by pretraining the model using a uniform random split of the dataset. In that case, the independence assumption will hold approximately after pretraining.
Another approach could be to share some parameters across the models.
A “load balancing” approach is also used where each model always keeps training on the data points assigned to it if not enough data points are assigned to it.
VAEs tend to be “overly inclusive” of the training distribution, i.e., they try to cover the entire support of the distribution.
GANs are prone to mode collapse where the model focuses only on one part of the distribution.
The proposed method provides a middle ground where the different generative models can focus on different parts of the distribution.
The experiments seem to be limited. The paper shows that their proposed setup improves over the VAE and GAN baselines.
For datasets, the paper uses two-dimensional synthetic data, MNIST and CelebA


I am currently studying at the University of Alberta, completing a bachelors of science with a specialization in Computing Science. Since beginning at the University in 2018, I have had the opportunity to learn many skills in Software Practice, Machine Learning, and Database Systems. Outside of my study of Computing Science, I have been able to study other subjects including ArcGIS and Economics. I had the opportunity to work during the summer of 2020 at EvMware, where I worked on writing web services in C# and SQL. Furthermore, I spent my second year working as a Resident Assistant at the University of Alberta, giving me the opportunity to strengthen my interpersonal skills. I am also currently heavily involved in the aviation community. I currently hold a Student Pilot Permit and have been able to fly a plane alone since before I could drive alone!
I am very capable in team settings and can consistently perform when presented with new challenges. Having previous experience in software development means that I have a strong understanding of industry practice and the development challenges faced in the industry. I can work independently and reliably deliver software on a continual basis thanks to previous experience. Having experience in aviation, I am detail-oriented and confident managing many tasks simultaneously.
Spent summer of 2020 developing web services.
Built strong interpersonal, time management, and organizational skills while leading about 50 students on their transition to university.
Detail-oriented, organized, and capable under pressure from having the opportunity to pursue a pilot's license.


CotEditor Help
coteditor.com
Discover CotEditor
Take a quick tour of CotEditor.
Tips
Check out some useful tips.
Preferences
Learn the options available in Preferences.
Solving Problems
Find the answers to common problems.
About CotEditor
Learn about CotEditor itself.
Index
Look up topics easily.


We help organizations manage the lifecycle of their mentorship
programs, from initial matchmaking to supporting those relationships
allowing you to provide ongoing support and continuous improvement.
Using Profiles and Machine learning to assist your match
mentors and mentees based on interests, skills, and location.
Helping you manage your mentorship program.
Support and tools
Create meetings with an interactive calendar, add notes, Zoom
links, record them as complete.
Profiles for Mentors/ Mentees
Profiles that allow users to showcase skills and interests


WITNESS LIST Financial Institutions Committee March 5, 2001-3:00P HB 1385 For: Boehm, John (Fulbright & Jaworski LLP) Leuschel, Jeff (Self) Rubottom, John (Lower Colorado River Authority) Utter, Tom (City of Corpus Christi) Registering, but not testifying: For: Grace, Cary (City of Houston) Igo, Shanna (Texas Municipal League) Lee, Donald (Texas Conference of Urban Counties) Scott, Betty (City of Fort Worth City Council) HB 1460 For: Carroll, Hank (Texas Cemeteries Association) HB 1493 For: Chase, Russ (Texas Association of Mortgage Brokers) Pair, Jim (Texas Association of Mortgage Brokers) Patillo, Dennis (Texas Association of Realtors) On: Pledger, Jim (Texas Savings and Loan Department) Temple, Larry (Texas Mortagage Bankers Assn.) Registering, but not testifying: For: Stinson, Bill (Texas Association of Realtors) HB 1608 For: Roberts, Michelle (Trust Financial Services Division of the Texas Bankers Association) Against: Saunders, Stephen (Self) Registering, but not testifying: For: Fletcher, Derek L. (Self) Against: McGehee, Jack (Texas Trial Lawyers Assn.) HB 1636 For: Chase, Russ (Texas Association of Mortgage Brokers) Patillo, Dennis (Texas Association of Realtors) Snider, John (Self) On: Estrada, Daniel (LBB) McKay, Ginny (Sunset Commission Staff) Registering, but not testifying: For: Pair, Jim (Texas Association of Mortgage Brokers) Sandberg, Eric (Texas Savings & Community Bankers Assn.) Stinson, Bill (Texas Association of Realtors) On: Mazur, Jeremy (Sunset Commission) Pledger, Jim (Texas Savings & Loan Department) HB 1914 For: Leuschel, Jeff (Self) Utter, Tom (City of Corpus Christi) Registering, but not testifying: For: Igo, Shanna (Texas Municipal League) Rubottom, John (Lower Colorado River Authority) HJR 9 For: Roberts, Michelle (Trust Financial Services Division of the Texas Bankers Association) Against: Saunders, Stephen (Self)


[3.0/4.0]
Many would consider the career of the public school teacher to be among the most thankless of occupations. Stories of idealistic individuals slogging through warlike conditions are sadly common. This setting provides the backdrop for this film, a searing dark comedy.
This film is clearly constructed to make a case for teachers. In the film, as in real life, teachers must face numerous obstacles. Students are apathetic, hostile, and even violent. Administrators are ineffectual, and oppressively burdensome with bureaucratic policies. Parents threaten litigation in response to attempts at discipline. In reality the situation is universally considered a tragedy, and this film, like many good works of literature, successfully spins comedy from tragedy.
The story begins by introducing Mr. Walmsley (John Davies), a man with a shady past connected with the circus. He accepts a position as a substitute teacher in a suburban public high school, a world perhaps more bizarre than the one he left. Being new, he finds that he is one of the few whose spirit has not been broken down by the system. He finds a kindred spirit in the art teacher, Ms. Germaine (Marsha Dietlein). Each in their own way fights a losing battle in their effort to make a difference in the lives of their students.
Eventually Walmsley realizes that there is no way to reach the students within the restrictions of the existing framework. He fashions an extreme scheme to address some of the most troubled students. He uses his circus background to fashion a more severe form of "detention", which is a common and ineffective punishment for students where they are required to stay after school hours and sit quietly in a classroom. It is only a slight form of punitive imprisonment. The approach taken by Walmsley will be jarring to many viewers, and director Andy Anderson states that it is meant to be comically disturbing.
The strength of the film is that the comedy is consistent (the story does not follow the cliche of becoming serious and humorless at the climax), and the comedy is often quite clever. The film is also unpredictable. Although the viewer is aware of where the journey will ultimately end, it is never clear how it will get there.
The primary weakness of the film is the transparent agenda that is being pushed. This could have been a problem if the film was not so entertaining. The other minor detriment is some of the acting. Although most of the adult roles were convincing, the students never emerge beyond typical adolescent stereotypes. However, the film is well written, and reflects the satirical wit of the filmmaker.
Highly recommended. The film sometimes uses jarring imagery, but this is to be expected in a black comedy. It is imaginative and funny, and it results in an enjoyable experience.
(c) 2000 Murali Krishnan The Art House Squatter http://ArtHouseSquatter.com
Sent via Deja.com http://www.deja.com/ Before you buy.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


17 Panels #panels
By default, all the .panel
does is apply some basic border and padding to
contain some content.
Example
Basic panel example

Basic panel example

Examples
Default styling
Panel title
Panel content
.panel-primary
Primary panel
Panel title
Panel content
.panel-success
Successful panel
Panel title
Panel content
.panel-info
Informational panel
Panel title
Panel content
.panel-warning
Warning panel
Panel title
Panel content
.panel-danger
Danger panel
Panel title
Panel content

Panel title

Panel content

Got Questions? ask them on the ASU Web Standards
Slack Channel


This Underrated Pair Of Sony Headphones Are Nearly Half Off Today
The Sony WH-XB900N wireless headphones are back on sale today in Amazon's Gold Box Deal of the Day, where you can get them for just $128. That is good for $120 off its regular price – nearly 50-percent off.
These headphones are pretty underrated, because when it comes to premium headphones, everyone looks at Sony's WH-1000XM3, which are some really good headphones. But the WH-XB900N are also really good. And at about $100 less (normally) than the WH-1000XM3, they are worth looking at. Now that they are nearly a third of the price of those headphones, these are the ones to buy.
The WH-XB900N are a really great pair of headphones. As you can likely tell by the name, these are part of the Extra Bass lineup. So you're going to get a ton of bass out of these headphones. That's great for those that like listening to bass-heavy music. And it is also adjustable using the Sony app, so if it's too much bass for you, then you can tone it down a bit.
Advertisement
Sound quality here is what you'd expect, and it's really good. After all, Sony is also a music label, so it's going to want its music to sound better on its own headphones. The mids and highs are crisp, while the bass is really low and thumping.
Battery life on the WH-XB900N is really good. We're looking at 30 hours on a single charge.
There is also noise cancellation available on the WH-XB900N. But where this differs from the WH-1000XM3 is the Ambient Sound, it's not here. So you're just getting noise cancellation, and that's going to be plenty for most people. You're still getting Amazon Alexa or Google Assistant support through these as well.
Advertisement
You can pick up the Sony WH-XB900N wireless headphones on sale from Amazon by clicking here.


Penetrating damp is generally taken to refer to water passing through the structure of a building from outside and is associated with a building defect. Common causes include leaking guttering, running overflow pipes and porous brickwork. Penetrating damp often creates dampness on internal surfaces that provides a strong clue as to the cause. Moss growth on external brickwork is also an indication of a potential penetrating damp problem.
The term penetrating damp is also used more broadly to include internal defects such as plumbing leaks and dampness affecting earth-retaining walls.


WELLINGTON PRIMARY SCHOOL
ROSPA HEALTH &
SAFETY GOLD AWARD
CLEMENTI N4C12
ROSPA HEALTH &
SAFETY GOLD AWARD
BEDOK N4C9
ROSPA HEALTH &
SAFETY GOLD AWARD
ANG MO KIO N2C31
ROSPA HEALTH &
SAFETY GOLD AWARD
BIZSAFE STAR
BCA GREEN AND GRACIOUS BUILDER AWARD
HDB CONSTRUCTION AWARD
BIZ SAFE PARTNER
HDB CONSTRUCTION SAFETY AWARD


How to Use
I recommend using a scale (TODO link) to measure ingredients since it is more accurate and saves time washing the dishes! [TODO Read about my kitchen setup for more details.]
|
|
|
|
|
|
|---|
Instructions


{% load comments cache %}
That's all the comments I'm taking for this post. Email me your thoughts<EMAIL_ADDRESS>Comments
{% if comment_list %} {% for comment in comment_list %} {% if comment.is_public %}{{ comment.submit_date|date:"P" }} on {{ comment.submit_date|date:"F jS, Y" }}
{{ forloop.counter }}
{% if comment.user_url %}{{ comment.user_name }}{% else %}{{ comment.user_name }}{% endif %} says...
{{ comment.comment|urlizetrunc:"60" }}Comments are closed.
That's all the comments I'm taking for this post. Email me your thoughts: blog+comments@ofbrooklyn.com.


Collection of functions to perform fixed and random-effects multivariate and univariate meta-analysis and meta-regression.
| Version: | 0.4.11 |
| Depends: | R (≥ 2.13.0) |
| Imports: | stats, graphics, grDevices, utils |
| Suggests: | metafor, meta, rmeta, nlme, MASS, dlnm |
| Published: | 2018-03-07 |
| Author: | Antonio Gasparrini |
| Maintainer: | Antonio Gasparrini <antonio.gasparrini at lshtm.ac.uk> |
| License: | GPL-2 | GPL-3 [expanded from: GPL (≥ 2)] |
| URL: | http://www.ag-myresearch.com/package-mvmeta |
| NeedsCompilation: | no |
| Citation: | mvmeta citation info |
| Materials: | ChangeLog |
| In views: | MetaAnalysis |
| CRAN checks: | mvmeta results |
| Reference manual: | mvmeta.pdf |
| Package source: | mvmeta_0.4.11.tar.gz |
| Windows binaries: | r-devel: mvmeta_0.4.11.zip, r-release: mvmeta_0.4.11.zip, r-oldrel: mvmeta_0.4.11.zip |
| OS X binaries: | r-release: mvmeta_0.4.11.tgz, r-oldrel: mvmeta_0.4.11.tgz |
| Old sources: | mvmeta archive |
| Reverse depends: | dosresmeta, mada |
| Reverse imports: | micemd, xmeta |
| Reverse suggests: | dlnm |
Please use the canonical form https://CRAN.R-project.org/package=mvmeta to link to this page.


We are experiencing technical difficulties :(
We are working to resolve them as soon as possible! Try again in a few minutes. If it is urgent, send an email to<EMAIL_ADDRESS>and tell us what you were trying to do, that we will do everything possible to help you.


How to Build an E-commerce App using Serverless ?
Learn how to build an ecommerce app using serverless architecture (reference architecture added). Start with an MVP and design the microservices using AWS Lambda functions to reap the benefits of event-driven architecture.
Serverless Architecture- the term which has been added to the technology stack, a few years ago, is becoming a common use since the launch of AWS Lambda in 2014.
The evidence is- Raymond Camden, Serverless Developer Advocate quoting that serverless will become ‘less of a hot new thing’ and more of a ‘just another option’ and it is becoming a reality now. One of the main reason is that it provocatively illustrates one of the biggest headaches of the IT industry- server administration. In short, write your code and run your applications without provisioning or managing anything on the server side.
But is the sweet reality is good as it is promised?
Well, let’s put this question on the test as we build an e-commerce app based upon serverless architecture using AWS stack.
Over the years, I’ve observed that many enterprises don’t know how to build such apps and ignore the massive benefits they have to offer due to insufficient knowledge. And that’s what I will be sharing with you in this post.
Features for Building an MVP for a Serverless E-commerce Application
Before we jump onto architecting the tech stack for our ecommerce app, let’s discuss some of the basic features for our MVP.
-
User authentication and authorization: The first feature that comes to your mind while you think of an ecommerce app is user pool authentication & management. The aim here is to provide a seamless registration process while demanding the least possible information.
-
Product Catalog: This consists of a product catalogue which will display the product list from where users can choose the product they want to buy.
-
Payment: This is a catalyst which can increase or decrease your sales ratio. The motive here is to provide multiple payments options with ease in process and the tight layer of security.
-
Shopping Cart: A shopping cart is an essential feature for an e-commerce app where users will be able to add the product of their choices and place an order.
-
Customer Support: Customer support is an inevitable feature to run a successful business. For this, you can either leave your contact details within the app or you may integrate a chatbot which will work based on instant messaging.
Apart from this, if you’re planning to outgrow your competitors, here are some additional features you should have a look upon:
-
Push Notifications: This is the single most key feature which is sought after by ecommerce giants. Plus, it works as a catalyst for the exclusive promotion and special offers.
-
Shipment Tracking: This feature tracks the status of the ordered shipment. Plus, it includes keeping the users updated by sending out SMS and email about the product delivery status.
-
Comparison Feature: This feature let users compare different product based on a fixed set of multiple values. For this, you will have to make another cart namely ‘Comparison Cart.’
Content
- Step1 - Monolithic First Approach
- Step2 - Why Serverless Architecture?
- Step3 - Identifying the Bounded Contexts
- Step4 - Designing Microservices
- Step5 - Architecting the Serverless Ecommerce App on AWS
Getting Started With Monolithic First Approach
According to Martin Fowler, there are two common patterns which are followed while people approach towards microservice architecture. Here are they:
-
Most of the successful microservice stories start with a monolith which scales up to a level and then breaks up into microservices.
-
Most of the development teams who started building their system with microservices have ended up in serious trouble.
When you begin building an application, there are a lot of uncertainties starting with how useful it will be for users. It may be hard to scale a system which is poorly designed but successful. However, that’s still a better option than being in the reverse situation.
And hence, it is always recommended to start with a simple version or MVP and test how well it is working out. And the add new functionalities in the form of microservices. Following this advice, here’s how what our MVP should consist of.
- Authorization — responsible for authorizing/managing a user
- Presentation — responsible for handling HTTP requests and responding with HTML or JSON/XML
- Business logic — the application’s business logic
- Database layer — data access objects responsible for accessing the database
- Application integration — integration with other services (e.g. via messaging or REST API). Or integration with any other Data sources
- Notification module — responsible for sending email/push notifications whenever needed
Why Serverless Architecture?
Six years ago, Walmart Canada failed on Black Fridays for two years in a row. It’s IT Department came across a multi-million dollar question since the ageing architecture started affecting their business negatively!
Monoliths might be fine for many systems but they are definitely not the best! And that’s where serverless architecture dominates the technology space.
But what happened with Walmart? Well, it couldn’t handle 6 million page views per minute and made it impossible to have a positive user experience. They had an architecture which was suitable for the internet of 2005, designed for desktops, laptops and monoliths.
That’s when they decided to re architect their legacy system to microservice based architecture and prepare themselves for the world by 2020, where there would be 4 Billion people connected to 25+ million apps available.
Sounds too good to be true? Wait! The results were:
- Conversions were up by 20% literally overnight
- Mobile orders were up by 98% instantly
- No downtime on Black Friday or Boxing Day
- The operational savings were significant as well since the company moved off of its expensive hardware onto commodity hardware
- They saved 40% of the computing power and experienced 20-50% cost savings overall
That was six years back. What we have now is to prepare ourselves till and beyond 2020. How? By building a serverless architecture based ecommerce mobile app! Here are a few more reasons to back my claims!
-
Enhanced scalability: Everyone expects their app to be the next Amazon. But if that happens, is it prepared to handle the load? Provisioning instance is a big risk- but so is being unprepared when success strikes.  One online program scaled to 50,000 users within six months without provisioning any servers.
-
Pay for what you use: Serverless technology saves money in terms of computing power and manpower both. First, you save by deleting all together with your infrastructure management staff and secondly, you pay only when your servers are running. No extra cost for idle time!
-
Decreased time to market: If your competitor got it first, you lose! That’s a simple economy of the consumer market. Serverless architecture allows developers to add new functionalities in hours and days instead of weeks and months. Few examples consist of apps that rely on third-party APIs for services like authentication (0Auth), social (Twitter), map (Mapbox), AI (IBM Watson) and more.
Characteristics of Microservices Before we move further adding new functionalities to our MVP, let us understand what makes a good microservice.
- Loose Coupling: One of the key features of what good microservices consists of is loose coupling. What this means is, a change in one service should not require a change in another. Because that’s the whole point of microservice, being able to change one service without needing to change anything to the other one.
For example, payment service and cart management should be developed as a separate service so that both can be scaled and maintained independently.
Apart from this, one service should know minimum things about the other services it is communicating with. This suggests keeping the number of calls with a different type of services to a minimum. If this fails, we will land up with potential performance problems on the top of tight coupling.
- High Cohesion: The second key feature of a good microservice is high cohesion. This means to group microservices based on similar behaviour. Why this? Because if we want to change one behaviour, we want to do it as fast as we can without having to change the behaviour of any other region.
If it requires to make changes in lots of different places and making lots of services, it is not only slower but potentially risky! And that’s what we want to avoid at all. And hence, this recommends defining fine boundaries within your architecture which ensures that similar behaviour resides in one place while it couples with other boundaries as loosely as possible.
Identifying the Bounded Contexts
We are developing a complex microservice architecture for a complex application which is going to be a set of loosely coupled bounded context i.e. business capabilities.
Microservices provides huge benefits but these benefits can be achieved only after a careful functional decomposition of the application into bounded contexts. The idea here is that any given application consists of multiple bounded contexts which can be defined as “a specific responsibility enforced by explicit boundaries” and consists of things which do not need to be communicated externally with other bounded contexts.
How to decompose an application into business capabilities?
By defining bounded contexts corresponding to business capabilities. These can be defined as something which does business in order to generate value. It normally corresponds to a business object,
- Product Management is responsible for product catalogue
- Notification management is responsible for email/sms notifications
Considering our case of a serverless ecommerce app, we are trying to introduce customer-centric services in order to meet our business goals of customer retention. Ecommerce is an inherently complex business domain. There are interrelationships among entities like users, products, reviews, payment, tracking, etc. But where do you start?
Following the Domain-driven Design (DDD) approach, we can first start by breaking the ecommerce domain down into subdomains. As a starting point, we can define our business units corresponding to the technical domain.
The business capabilities of a serverless ecommerce app will include:
-
User Management: This is responsible for user authentication and management through social gateways, manual authentication, email verification and mobile number verification.
-
Content Management: This is responsible for search bar, navigation and translation services.
-
Product Catalog: This is responsible for product details, product images and product reviews.
-
Customer Management: This is responsible for catering facilities like personal subscriptions, saved payment methods, address verification, ship orders, customer credits and loyalty programmes.
-
Cart Management: This is responsible for shopping cart management, quick order and check out.
-
Payment Management: This is responsible for processing payment and fraud tracking.
-
Inventory Management: This is responsible for keep tracking of back orders and pre-orders.
-
Track Management: This is responsible for keep track of shipment and notifying of orders via push notifications, SMS and email.
-
Reporting Management: This is responsible for web analytics, business intelligence, product sales reports and many more.
-
Marketing Engine: This is responsible for personalised marketing and management of recommending products.
Designing Microservices
Remember the MVP that we built earlier? It will give us a starting point to think about how to classify the services which are required to build a full-fledged serverless ecommerce application.
The Application Frontend and Product Catalog subdomains are highlighted since they are an integral part of the solution. Furthering with our DDD approach, we’ll define more bounded contexts that emerge from these subdomains.
Within the Customer UI part, two distinct bounded contexts come into play. The web and the mobile application frontend shares a common language between each other. We will call these context “Web App” & “Mobile App”.
Product Related Services is even more divided. Product information- containing the core information and images about products- forms a Product Information bounded context. Another distinct bounded context that emerges from this domain is Personalised Marketing which communicated directly with customers. Lastly, there is a Customer Identity context focused exclusively on user security and access control.
The payment subdomain dissolved into Payment Service bounded context which handles payment process, financial security and fraud tracking. Check out domain breaks down into Checkout & Order Tracking bounded context. Lastly, inventory domain dissolves into Inventory Management bounded context.
Now that we have established the bounded contexts, we can now think about what services will make sense from the user/customer perspective. As mentioned earlier, Application Frontend part has two distinct services: Mobile App & Web App.
The Customer Identity context should provide Customer Authentication service in order to ensure that only authorised user is being able to access our services. Plus, Customer Management which will handle personal subscriptions, saved payment methods, address verification, customer credits and loyalty programmes.
The Product Information context will take care of product catalogue, product images. This will also handle product reviews. This task will be cross-referenced and will be supplied customer identity from Customer Authentication services.
The Personalised Marketing service will handle recommending products as per customer’s previous choice, preferences and purchases. It will also handle the content management service which will include navigation and translational services.
For the Payment domain, we will only refer to a single eponymous service per bounded context. The Order Tracking & Checkout will house two distinct services, first Track management which will keep track of shipment and notifying of orders via push notifications, SMS and email and secondly, Cart management which handle quick order and checkout.
Lastly, the Inventory domain will house Inventory management which will keep track of orders and pre-orders. And, reporting service which will be responsible for generating various reports and monitoring.
The last step in this design process is to illustrate the interactions that will take place between the services. However, we will depict them at the time of fabricating our app architecture. Before moving onto that, there is one important thing that one must keep in mind:
- Do not assume that all these services and interactions will take place in real time while servicing the end user activity. Few services will need information from a few other services, an event-based approach combined with caching could be used to ensure that its processing is self-contained in order to cater user’s request in real time.
Architecting the Serverless Ecommerce App on AWS
Now that we have built our individual microservices, it is time now to figure out our tools. All these services can leverage service discovery and will use simple mechanisms (such as HTTP) for easy connectivity from a wide variety of devices.
Considering the granularity level of serverless functions, we will depict three main portions up to the serverless level viz. Recommendation Engine, Cart Management and Payment Service. For the simplicity of representation, rest of the application will be discussed up to the microservice level.
Maintaining an e-commerce application with a heavy product catalogue is challenging. The catalogue should be searchable, and individual product pages should contain rich information including product images, pricing and reviews.
Here customers may want to find things they are interested in and expects pages to load quickly. Also, customers expect the ‘Quick Checkout” functionality to be able to make purchases any time they want.
The customer-facing user experience in you in your ecommerce workloads includes responsive web applications and optionally as native mobile applications. In combination with the backend architecture, you build your application by assembling multiple frontend components that correspond to and communicate with functions through backend APIs.
Here’s an example of how frontend of our serverless ecommerce app will work.
The picture shows the Product Catalogue management microservice which will use Amazon Simple Storage Service. This will accommodate all static content, such as product images, manuals and videos. The main benefit of hosting the website on S3 is that it can be replicated to multiple regions of AWS.
Amazon DynamoDB will accommodate persistent session data such as shopping cart and product pricing details. Since it is a NoSQL database, we have the flexibility to add new product categories and new attributes to our catalogue.
We will use Amazon API Gateway for the frontend to access the backend features and compute these functionalities either as microservice or functions (whichever we have chosen). One important reason for using multiple API Gateways is to avoid creating a single monolithic API Gateway as we scale at a higher level. More to that, single API Gateway will the autonomy provided by the microservices architecture.
The DNS requests to the e-commerce app will be handled by highly avaiable Amazon Route 53. While Amazon CloudFront is used as our content distribution network. It will cache static and streaming content with low latency from locations close to the customer.
Amazon Elasticache will be used as a session store for volatile data and as a caching layer for the product catalogue to reduce input/output and cost over DynamoDB. The Product Catalogue will be fully loaded into Amazon CloudSearch which will provide full search functionality to the customers.
Amazon Translate will be used for facilitating our app in multiple languages. For sending transactional email, such as order confirmation, to the customer, we will Amazon SES. This can be easily added with the help of AWS SDKs.
As discussed earlier, we will use functions for three main services- payments, cart management and Recommendation Engine. Rest of the microservices will be hosted on EC2 Instances while Elastic Beanstalk will automatically handle the details of provisioning, load balancing, auto-scaling and application health monitoring.
Using Serverless in our Ecommerce App
#1. Cart Management
The Cart Management business functionality will make use of 5 functions:
- ADD (Function 1): This will be triggered when a user adds a product into their cart.
function getProductData(profile_id,cb){
var params = {
TableName : TABLES.productData,
KeyConditionExpression: "#id = :profile_id",
ExpressionAttributeNames:{
"#id": "profile_id"
},
ExpressionAttributeValues: {
":profile_id": profile_id
}
};
docClient.query(params,function(err,data){
if(err){
console.log('err in getProduct');
cb(err,null)
} else {
cb(null,data.Items);
}
});
}
- DELETE (Function 2): This will be triggered when a user deletes a product from their cart.
function deleteProduct(id,product_id,cb){
var params = {
TableName: TABLES['productCatalog'],
Key:{
id,
product_id
}
};
docClient.delete(params,function(err,data){
if (err) {
console.error("Unable to delete item. Error JSON:", JSON.stringify(err, null, 2));
cb(err);
} else {
console.log("DeleteItem succeeded:", JSON.stringify(data, null, 2));
cb(null);
}
});
}
-
QUANTITY(Function 3): Whenever the number of product quantities will be updated, this function will be triggered.
-
UPDATE (Function 4): This function will be running at a set interval of time to make sure that the products added in the cart are available in the warehouse. If not, it will automatically delete them.
-
CHECK OUT (Function 5): Once the order is confirmed, this function will be triggered which will initiate the payment procedure.
#2. Payment Management
For facilitating the payment system, we will use Stripe Relay and Stripe Checkout. The function level granular architecture of serverless payment system will look something like this:
- Once the user creates an order, the app will provide order details to Stripe.
- After confirming the product availability in the Inventory (DynamoDB), Stripe confirms the order and returns with the order total to the user.
- Once the order is confirmed, an AWS Lambda function will be triggered which will load Stripe Checkout over a secured HTTPS URL.
- Once the user provides the payment details, Stripe indicates the payment result to the user.
Accepting the card payment with Stripe is a two-step process which works on client-side and server-side both:
-
From the user’s browser, Stripe securely collects the user’s payment details and returns a representative token which is then submitted by the browser to the app’s server.
-
Once the request is submitted, an API request is sent which triggers a Lambda function which will create a charge and complete the payment
Step 1: You can securely collect the payment information with Checkout. It combines with the HTML, JavaScript and CSS to create an embedded form as shown below. To get started, add the following code to your secure payment page and makes sure that your code submits to your own server-side code:
<form action="your-server-side-code" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_6pRNASCoBOKtIshFeQd4XMUh"
data-amount="999"
data-name="Stripe.com"
data-description="Example charge"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="auto"
data-zip-code="true">
</script>
</form>
Step 2: Once a token is created, your server-side code will trigger a Lambda function through API Gateway to create a one-time charge. This request contains the token, currency, amount to charge and any additional information you want to pass.
Our Lambda function will look something like this. It is written in Node.js
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2"); // Token is created using Checkout or Elements!
// Get the payment token ID submitted by the form:
const token = request.body.stripeToken; // Using Express const charge = stripe.charges.create({
amount: 999,
currency: 'usd',
description: 'Example charge',
source: token,
});
Tokens can be only used once and every time the user purchases something, he/she will have to re-enter the details.
However, Stripe allows multiple payment solutions like saving the card details, payment through bank, refunds and much more. You may check out their documentation here for adding further functionalities to your payment mechanism.
#3. Recommendation Engine
The main aim of developing a recommendation engine is to facilitate each user with content based on unique interests of each visitor to boost user engagement and improve user conversion, lifetime value and retention.
The recommendation engine will use the following data:
- Actions: This will include user clicks, visited products and products categories. This will help in constructing an accurate model and make better content recommendation.
- Behaviours: This will include users’ past preferences and information from which predictive models will learn more about their preferences. *Demographic Information: This will include user-specific information such as gender, age, etc. which will help in prediction as users with similar background tends to have similar choices.
The recommendation engine uses AWS Lambda for processing users’ actions as mentioned above. It extracts events from multiple sources. As soon as the event occurs, it triggers a specific Lambda function attached to it which process the event further.
Once the event is processed, it checks for users’ history in DynamoDB which is used to store aggregated data of clicks by the user, product and preferences as well as user profile information.
If the users’ history exists, machine learning through predictive analysis determines the content affinity scores and passes the information to Kinesis Stream.
Content affinity scores are used with the personalised logic to determine the most relevant content to present to the user.
ElastiCache is used to store pre-computed cold-start content affinity scores. This stores the logic for the users who have no history, means they are visiting for the first time. While Machine Learning helps us in easily designing, test, operationalize and manage predictive analytics solutions due to its API-driven architecture.
Once the events are processed, they are fed to Kinesis Stream which aggregates clicks in near real-time by the user, product choices, preferences and demographic information to write to DynamoDB. It also archives the raw click-stream data to S3.
Maintaining the Data Consistency
Since microservices are architectured with ‘Database per Service’ philosophy, maintaining the data consistency is highly critical. Especially considering our case of an ecommerce app, there are going to be multiple services that need to ensure data consistency across services.
When you’re working with microservices, things are quite complicated. Since microservices has its own database, you can no longer use two-phase-commits to have database consistency across your system.
Let’s take an example: whenever a user places an order, charging the customer, sending the confirmation, updating the stock and sending it out to delivery doesn’t happen in a single ACID transaction. To execute the above scenario, you’ll be required to create a distributed transaction.
What’s the solution?
Well, we will implement each business transaction that spans across multiple services as a Saga.
Saga can be defined as a sequence of local database transaction updates the data within each service database. The first trigger will be an external one while the subsequent triggers will be initiated by the completion of the preceding service.
For implementing a saga transaction, we will you the method of events/orchestration. In this method, there is absence of central coordination. The first service initiates a transaction and then publishes an event. Further, this event is listed by the next service which executed a local transaction and publish an event, depending upon the requirement.
- Once the order is placed, the order service saves your order with a pending state and publishes this event- EVENT_ORDER_CREATED
- The inventory service listens to the event- EVENT_ORDER_CREATED, prepares the order after verifying with the stock available, update it and publish the next event- EVENT_ORDER_PREPARED
- The payment service listen to the event- EVENT_ORDER_PREPARED, bills the customer and publish the next event- EVENT_ORDER_BILLED
- Delivery service listens to this event- EVENT_ORDER_BILLED, will pick up products from the warehouse and deliver the product. Once the product is delivered, it publishes the next event- EVENT_ORDER_DELIVERED
- In the end, order service, which initiated this transaction, listen to EVENT_ORDER_DELIVERED and concludes the order state as finished.
If at all, during this transaction, a business rule is violated, then the saga executes a series of compensating transactions to undo any preceding changes. Here’s an example, what if payment fails?
- If the payment service fails to execute successfully, it will produce an event- EVENT_PAYMENT_FAIL
- The preceding event- EVENT_ORDER_PREPARED and EVENT_ORDER_CREATED listens to this and terminates the created order.
The flexibility, independent scalability, event-driven nature and strong encapsulation in serverless architecture makes it a perfect choice for building an e-commerce application. But successful implementation of serverless for e-commerce app will require a minimum level of expertise and maturity in terms of function observability, monitoring and automation.


|
|
|
| < Previous PageNext Page > |
Java 1.3.1 code compiled with Java 1.4.1 compiler fails.
Description:
Compiling some Java 1.3.1 code with the 1.4.1 version of javac causes a
java.lang.NoSuchMethodError when it is run. Java 1.4.1 adds an
append(StringBuffer)
method to the StringBuffer
class.
If you have code that calls append
with a StringBuffer as an
argument, the Java 1.3.1 version of javac compiles this to
append(Object)
. The Java 1.4.1 version of javac compiles
the same code to append(StringBuffer)
. Running the version compiled
with the Java 1.4.1 version of javac in a Java 1.3.1 environment
returns a java.lang.NoSuchMethodError.
Resolution:
To get your code to run on Java 1.3.1, just cast your argument to
java.lang.Object
. For example, append((Object)
stringBufArg)
| < Previous PageNext Page > |
Last updated: 2003-06-11
|
Get information on Apple products. Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2004 Apple Computer, Inc. All rights reserved. | Terms of use | Privacy Notice |


At Elimu Bora we intergrate technology into education delivery making it interesting to learn ,memorable and more interactive. We believe that every child deserves a chance to get good quaility education for a better life
Seamless interaction with the affordable teaching board on any surface
Better and faster delivery of content improving the quality of education
Learning just got interesting, more engagement between learner and teacher leads to better understanding
Monitor the progress with valid data points and make decisions on staffing and areas of improvement , the content delivered and time needed to complete the syllabus
+254 727468088
+254 724655587
info.hisiboards@gmail.com


Sorry, but the term '{{term}}' can't be found/have not been mapped yet by pydoc.me.
If you feel it ought to, please have a look at the sources, it's a fairly straight-forward Flask app. Pull requests highly welcome, or you could always try logging an issue and if I find it appropriate and reasonably easy I might add it for you.


Classes for parsing and creating JSON encoded values. These classes parse strings of JSON encoded values into Java accessible objects. They also support programmatically building JSON strings.


Download Bentley View V8i Selectseries 4 08 11 09 829
Bentley View V8i Selectseries 4 0 Free Download
Bentley View V8i Free Download
Bentley View 8 1 Download Free Bentleyview Exe
Bentley View V8i Selectseries 3 Latest Version Get Best Windows Software
Bentley View Connect Edition Update 13 Download Archsupply Com
2019 01 27 Bentley View Connect Edition Update11日本語版リリースしました Microstation Wiki 日本語 Microstation Bentley Communities
Bentley View Download Fasrpeople
Bentley View V8i Selectseries4 Installation Project Review Forum Project Review Bentley Communities
Microstation Mikrostation Description And Microstation Dgn Format
1b Bentley View Installation Youtube
Bentley View V8i 08 11 09 Error 1603 Product Administration Forum Product Administration Bentley Communities
2019 08 28 Bentley View Connect Edition X64 Ses Update 13 10 13 00 48日本語版 Fullビルド ランゲージパックがリリースされました Microstation Wiki 日本語 Microstation Bentley Communities
Microstation J Download Avidnew
Microstation V8i Download For Students Peatix
Bentley View Ss4 No Longer Free For Non Select Users Licensing Cloud And Web Services Forum Licensing Cloud And Web Services Bentley Communities
Bentley Microstation V8i Download Mac Peatix
Https Www Iowadot Gov Contracts Bentley Product Quickstar 20information Pdf
Microstation V8i Download Full Versionl Carris S Weblog
Download Bentley View V8i Selectseries 4 Latest Version Windows For Pc 2021 Free Appsfire
01 Bentleyソフトウエアダウンロードガイド 新 Microstation Wiki 日本語 Microstation Bentley Communities
Https Forums Autodesk Com Autodesk Attachments Autodesk 706 156458 1 3d Pdf With Bentley View Pdf
Bentley Microstation V8i Free Download Getintopc Free
Bentley Microstation Software Free Download Link Cagfulbhildcount S Ownd
Bentley View V8i Selectseries 4 Driver Download
Dgn Viewer Microstation Forum Microstation Bentley Communities


How does it work?
Enter your running goal. All the information the skill needs is the distance you are planning to run and the date when you plan to achieve it.
Add the skill to your morning routine or open it daily to get the running instructions. Record your training with any smart device and sync it with Strava for personnalized feedback.
Follow the plan until your race day and enjoy seeing your progress. 3 days prior to the race, you will also get few nutrition tips to perform at your best.
"Open Running Plan"
"Ask Running Plan to configure"
"Ask Running Plan coming workout"
"Ask Running Plan previous session"
"Ask Running Plan to link Strava"
"Ask Running Plan to buy warm up plan"
Get the full experience on your Amazon Echo or other Alexa-enabled devices.


This test requires Chrome 69 or higher.
We're going to render 10,000 elements, remove half of them and re-sort, then re-sort again while adding them back in.
Worthy of note: I had a request to benchmark against morphdom. The reason you don't see it here is because, when I tried to write a test case for it, it turned out that manipulating the real DOM was so incredibly slow and expensive that it repeatedly crashed the browser, even when I attempted to reduce the test load by 90%. I have a suspicion that you would get the same result from any real-DOM manipulation library. Also, I'm aware that the output from the virtual-dom test is super crazy. I'm not sure if I'm doing something wrong or if virtual-dom just doesn't quite understand how to handle this scenario (docs are hard to find). I welcome any tips. These are just informal benchmarks after all.


Macau WTCR: Andy Priaulx takes first WTCR win of 2019 in finale
by Jack CozensThree-time World Touring Car champion Andy Priaulx claimed his first WTCR victory by holding off Rob Huff in a tense Macau Guia Race finale.
Priaulx, who has two previous Guia Race wins from his World Touring Car Championship spell, set up victory by passing polesitter Huff before the approach to the Reservoir kink at the start of the race.
While Huff's Sebastien Loeb Racing Volkswagen Golf GTI was able to close up in the second and third sectors on the mountain, Priaulx had superior drive off the Melco hairpin each time and was able to gap his compatriot through the final sector in preparation for the long run to Lisboa.
Huff set the fastest lap on the antepenultimate tour in one final bid to close in on Priaulx, but the Cyan Racing Lynk & Co 03 driver went faster still on the following lap, taking a big step towards winning the race in doing so.
Huff closed up once more in the third and fourth sectors, but Priaulx held tight to clinch his first World Touring Car win in nine years - which also denied Huff a record 10th win in Macau.
Jean-Karl Vernay jostled with Huff and his team-mate Johan Kristoffersson at the start, settling into third place at Lisboa in his WRT Audi RS3 LMS.
Both he and Kristoffersson kept a watching brief behind the two leaders, as the leading quartet finished well clear of the rest of the pack.
Thed Bjork had occupied fifth, but dropped two positions on the final lap to boost Cyan team-mate Yvan Muller's points haul.
BRC Racing Hyundai driver Nicky Catsburg instead claimed the place, having kept Muller behind him as the leading pack escaped.
The fourth Lynk & Co 03 of Yann Ehrlacher came home in eighth, behind Muller and Bjork, while Kevin Ceccon (Alfa Romeo Giulietta Veloce) and title hopeful Esteban Guerrieri completed the top 10.
Points leader Norbert Michelisz was 12th in his BRC Racing Hyundai. He now has nine points in hand over Munnich Motorsport Honda driver Guerrieri, while Muller is two further back in third after winning the first two races of the weekend.
Result - 11 laps
| Pos | Driver | Team | Car | Gap |
|---|---|---|---|---|
| 1 | Andy Priaulx | Cyan Performance | Lynk & Co | 27m58.428s |
| 2 | Rob Huff | SLR VW Motorsport | Volkswagen | 0.352s |
| 3 | Jean-Karl Vernay | WRT | Audi | 1.193s |
| 4 | Johan Kristoffersson | SLR Volkswagen | Volkswagen | 1.855s |
| 5 | Nicky Catsburg | BRC Racing Team | Hyundai | 20.030s |
| 6 | Yvan Muller | Cyan Racing | Lynk & Co | 21.096s |
| 7 | Thed Bjork | Cyan Racing | Lynk & Co | 21.552s |
| 8 | Yann Ehrlacher | Cyan Performance | Lynk & Co | 22.194s |
| 9 | Kevin Ceccon | Team Mulsanne | Alfa Romeo | 22.565s |
| 10 | Esteban Guerrieri | Munnich Motorsport | Honda | 23.506s |
| 11 | Tom Coronel | Comtoyou Cupra | Cupra | 23.884s |
| 12 | Norbert Michelisz | BRC Squadra Corse | Hyundai | 24.597s |
| 13 | Niels Langeveld | Comtoyou Audi | Audi | 25.054s |
| 14 | Luca Engstler | BRC Racing Team | Hyundai | 27.552s |
| 15 | Frederic Vervisch | Comtoyou Audi | Audi | 27.925s |
| 16 | Attila Tassi | KCMG | Honda | 29.259s |
| 17 | Nestor Girolami | Munnich Motorsport | Honda | 30.140s |
| 18 | Mehdi Bennani | SLR VW Motorsport | Volkswagen | 30.527s |
| 19 | Tiago Monteiro | KCMG | Honda | 31.317s |
| 20 | Mikel Azcona | PWR Racing | Cupra | 31.500s |
| 21 | Benjamin Leuchter | SLR Volkswagen | Volkswagen | 32.319s |
| 22 | Ma Qing Hua | Team Mulsanne | Alfa Romeo | 34.602s |
| 23 | Aurelien Panis | Comtoyou Cupra | Cupra | 37.051s |
| 24 | Robert Dahlgren | PWR Racing | Cupra | 39.756s |
| 25 | Billy Lo | Tian Shi Zuver Team | Audi | 51.272s |
| - | Gabriele Tarquini | BRC Squadra Corse | Hyundai | Retirement |
| - | Gordon Shedden | WRT | Audi | Withdrawn |
| - | Jim Ka To | KC Motorgroup | Honda | Withdrawn |
Drivers' championship
| Pos | Driver | Points |
|---|---|---|
| 1 | Norbert Michelisz | 316 |
| 2 | Esteban Guerrieri | 307 |
| 3 | Yvan Muller | 305 |
| 4 | Thed Bjork | 288 |
| 5 | Yann Ehrlacher | 209 |
| 6 | Gabriele Tarquini | 205 |
| 7 | Mikel Azcona | 199 |
| 8 | Nestor Girolami | 198 |
| 9 | Rob Huff | 197 |
| 10 | Jean-Karl Vernay | 196 |


Hi! I heard you wanted a tab?
In between these brackets is a tab character. Just copy and paste!
[ ]
Enjoy!


Controls: 'A', to move the piece to the left 'D', to move the piece to the right 'S', to move the piece down 'W', to rotate the piece clockwise 'X', to rotate the piece counterclockwise