顯示具有 rails 標籤的文章。 顯示所有文章
顯示具有 rails 標籤的文章。 顯示所有文章

2016年5月2日 星期一

Nested transactions



User.transaction do
  User.create(username: 'Kotori')
  User.transaction do
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end
both Koori, Nemu created

As mentioned previously ActiveRecord::Rollback does not propagate outside of the containing transaction block and so the parent transaction does not receive the exception nested inside the child. Since the contents of the child transaction are lumped into the parent transaction both records are created! I find it easier to think of nested transactions like the child who dumps its contents into the parent container, leaving the child transaction empty.

User.transaction do
  User.create(username: 'Kotori')
  User.transaction(requires_new: truedo
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end

only “Kotori” is created 

2016年4月30日 星期六

Thread-Safety


Rack::Lock => make sure thread safe
def threadsafe!
  @preload_frameworks = true
  @cache_classes      = true
  @dependency_loading = false
  @allow_concurrency  = true
  self
end
sometimes on production rails will remove Rack::Lock => because Web server will handle it => unicorn, Fusion Passenger => single thread

puma => multi thread 

Rails Middleware Walkthrough


ActionDispatch::Static => provide static file on public
Rack::Lock => lock the app down to a single thread
ActiveSupport::Cache::Strategy::LocalCache::Middleware => cache method based on ActiveSupport::Cache::FileStore
easy to use by Rails.cache.read or Rails.cache.write
Rack::Runtime => sets an X-Runtime response header to show spending time
Rack::MethodOverride => if set params[:_method], could reset http method, ex put, delete on form
ActionDispatch::RequestId => set unique request id
Rails::Rack::Logger => log when request start, and request end
ActionDispatch::ShowExceptions & ActionDispatch::DebugExceptions => rescue error, and custom format
ActionDispatch::RemoteIp => detect ip attack
ActionDispatch::Reloader => reload classes in development mode
ActionDispatch::Callbacks =>  We can call before or after methods on this and pass in a block which will then be triggered on each request.
ActiveRecord::ConnectionAdapters::ConnectionManagement => clear db connections
ActiveRecord::QueryCache => active record query cache
ActionDispatch::Cookies, Session::CookieStore and Flash =>  set cookie, sessions
ActionDispatch::ParamsParser => prepare params
ActionDispatch::Head => transfer head request to get
Rack::ConditionalGet and Rack::ETag => set ETag header based on the response body, conditional => if not changed, not process => only send 304

ActionDispatch::BestStandardsSupport => add recommend browser to client 

LOG TAGGING IN RAILS

My::Application.config.log_tags = [ :uuid ]

Now you can filter the log content by a particular request ID to see all output related to a single request 

2016年4月19日 星期二

How key-based cache expiration works




  1. The cache key is fluid part and the cache content is the fixed part
  2. key is calculated from the content
  3. when key changes, simply write the new content to new key
  4. will generate a lot of cache garbage => don’t care => Memcached will automatically evict the oldest keys first when it runs out of space
  5. You deal with dependency structure by tying the model object together on updates
  6. The caching itself then happens in the views based on partials rendering the objects in question

2016年4月17日 星期日

ActiveRecord::Observer


callback, but not very related to model
we can use observer

class AuditObserver < ActiveRecord::Observer
  observe :account:balance

  def after_update(record)
    AuditTrail.new(record"UPDATED")
  end
end


Storing Observers in Rails

If you’re using Active Record within Rails, observer classes are usually stored in app/models with the naming convention of app/models/audit_observer.rb.

Configuration

In order to activate an observer, list it in the config.active_record.observers configuration setting in yourconfig/application.rb file.
config.active_record.observers = :comment_observer, :signup_observer

Observers will not be invoked unless you define these in your application configuration. 

2016年4月10日 星期日

Rails 啟動過程

1 啟動!
  1. call ruby rails in RVM folder => railties/bin/rails => require "rails/cli"
  2. railties/lib/rails/app_rails_loader.rb : find ‘bin/rails’
  3. bin/rails: require_relative '../config/boot and require 'rails/commands'
  4. config/boot.rb: setting Bundler, Gemfile
  5. rails/commands.rb: setting aliases, require 'rails/commands/commands_tasks'
  6. rails/commands/command_tasks.rb: run command, => rails/commands/server => require ‘fileutils, ‘optparse', ‘action_dispatch’, require 'rails'
  7. actionpack/lib/action_dispatch.rb: response routes
  8. rails/commands/server.rb: inherited from Rack::Server: call Rack::Server’s initialize。
  9. Rack: lib/rack/server.rb: provide interface for app on rack base, => setting options
  10. config/application.rb: app settings
  11. Rails::Server#start: still will call Rack::Server.start
  12. config/environment.rb
  13. config/application.rb => require ‘rails/all'

2 載入 Rails

  1. railties/lib/rails/all.rb
  2. config/environment.rb
  3. railties/lib/rails/application.rb: initialize!
  4. Rack: lib/rack/server.rb

2016年3月30日 星期三

Stubs, Mocks, and Spies

stubs:
create fake object, and assume this fake object can response what information, response, make sure the main tested object can get the consistent result

mocks
same with stubs, but must be executed




 from https://danielzhangqinglong.github.io/2015/04/07/rspec-mock/

比如說, 現在要去測試一下Twitter是不是成功發了一條推文:
1
2
3
4
5
6
7
8
9
10
require 'twitter'
class TwitterEmotion
def tweet
twitter_client.update "I am very happy"
end
def twitter_client
Twitter::REST::Client .new
end
end
測試代碼:
1
2
3
4
it "should tweet successfully" do
emotion = TwitterEmotion. new
expect{ emotion.tweet }.not_to raise_error
end
但是這真的會把你的推文發生出去,還有,如果網絡環境不好或者Twitter的服務器沒有及時處理請求,那麼我這邊的測試就會跑不通,等等問題. 
正因為如此,才需要把使用Twitter服務的部分用mock代替.這樣測試就不會和Twitter進行交互了.下面是使用mock後的測試:
1
2
3
4
5
6
7
8
9
10
11
12
it "should tweet successfully" do
# mock Twitter client
twitter_client_mock = double( 'Twitter client' )
# 讓update方法可以在mock對像上調用
allow(twitter_client_mock). to receive(:update)
emotion = TwitterEmotion. new
allow(emotion). to receive(:twitter_client).and_return(twitter_client_mock)
# 如果在emotion上調用了twitter_client, 那麼就返回twitter_client_mock
expect{ emotion.tweet }.not_to raise_error
end



allow (想要替換方法的對象) . to receive (所要替換的方法) . and_return (返回值對象)


allow
(mock對象) . to receive (方法) . and_raise (異常)

Initialize the serializer

ActiveModel::ArraySerializer.new(Funding.all, each_serializer: FundingSerializer).to_json




For me I passed the view_context to the array serializer:
ActiveModel::ArraySerializer.new(your_array, each_serializer: YourSerializer, scope: self.view_context)

how to pass scope

https://github.com/rails-api/active_model_serializers/issues/510




2016年2月27日 星期六

Making Generators in Rails 


class LayoutGenerator < Rails::Generators::Base
  source_root File.expand_path('../templates'__FILE__)
  argument :layout_name:type => :string:default => "application"
  class_option :stylesheet:type => :boolean:default => true:description => "Include stylesheet file"

  def generate_layout
    copy_file "stylesheet.css""public/stylesheets/#{file_name}.css" if options.stylesheet?
    template "layout.html.erb""app/views/layouts/#{file_name}.html.erb"
  end

  private
  def file_name
    layout_name.underscore
  end

end

every public method in the class (Rails::Generators::Base) will be executed when the generator runs

  • arguments optional:  we can define a default value for each argument. We’ll add a layout_name argument with a default value of application
  • template method: which takes similar arguments to copy_file but which will parse any erb in the template before copying it to the destination directory
  • class_option: the ability to pass an option that will stop the stylesheet file being generated. We can do this by using the class_option method







2016年2月18日 星期四

Reading Rails - Concern 


major feature:
  1. auto extend ClassMethods and included InstanceMethods
  2. Dependency resolution

  • Included

Module defines the callback included which is called when a module is included into another class or module

if both two module are extend ActiveSupport::Concern, and both have included do 
module Named
  extendActiveSupport::Concern
  included do
     base.validates_presence_of:first_name,:last_name
  end#...
end

module Mailable
  extendActiveSupport::Concern
  include Named
  included do
     email_regexp =/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/base.validates_format_of:email,with:email_regexp
  end#...
end


Concern delays calling these blocks until your module is included in something that is not a Concern.

  • Class Methods

Calling include only mixes in the instance methods

If there is a ClassMethods module in a Concern, it will automatically extend whatever it is included in.
module Exclaimable
     def self.included(base)
          base.extend(ClassMethods)
     end
     module ClassMethods
          def shout!
               puts"Look out!"
          end
     end
end


  • How It Works

module ActiveSupport
  module Concern
    def self.extended(base)
      base.instance_variable_set("@_dependencies", [])
    end

    def append_features(base)
      if base.instance_variable_defined?("@_dependencies")
        base.instance_variable_get("@_dependencies") << self
        return false
      else
        return false if base < self
        @_dependencies.each { |dep| base.send(:include, dep) }
        super
        base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
        base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
        base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
      end
    end

    def included(base = nil, &block)
      if base.nil?
        @_included_block = block
      else
        super
      end
    end
  end
end

Concern delays calling included blocks and mixing in ClassMethods by keeping track of modules in @_dependencies. When a Concern is included in another class, it triggers all the logic on that class.

While unpacking how Concern works, we also came across some other interesting things:
  • Module defines included and extended callbacks.
  • Ruby provides methods such as instance_method_get to access an object's internals.
  • Class methods are not mixed in with include.
  • include takes any number of arguments: include ModA, ModB, ModC.
  • Classes can be compared with equality operations: ClassA < ClassB.
  • By convention, anything starting with a capital letter is a constant.