turboboost 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,111 @@
1
+ module Turboboost
2
+ module Controller
3
+ extend ActiveSupport::Concern
4
+
5
+ CATCHABLE_ERRORS = {
6
+ 'EOFError' => 500,
7
+ 'Errno::ECONNRESET' => 500,
8
+ 'Errno::EINVAL' => 500,
9
+ 'Timeout::Error' => :request_timeout,
10
+ 'Net::HTTPBadResponse' => 500,
11
+ 'Net::HTTPHeaderSyntaxError' => 500,
12
+ 'Net::ProtocolError' => 500,
13
+ 'ActiveRecord::RecordNotFound' => :not_found,
14
+ 'ActiveRecord::StaleObjectError' => :conflict,
15
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
16
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
17
+ 'ActiveModel::StrictValidationFailed' => :unprocessable_entity,
18
+ 'ActiveModel::MissingAttributeError' => :unprocessable_entity
19
+ }
20
+
21
+ included do
22
+ send :rescue_from, *(CATCHABLE_ERRORS.keys), with: :turboboost_error_handler
23
+ end
24
+
25
+ def turboboost_error_handler(error)
26
+ if request.xhr? && request.headers['HTTP_X_TURBOBOOST']
27
+ error_status = CATCHABLE_ERRORS[error.class.name]
28
+ response.headers['X-Turboboosted'] = '1'
29
+ if defined?(error.record)
30
+ render_turboboost_errors_for(error.record)
31
+ else
32
+ translation = I18n.t("turboboost.errors.#{error.class.name}")
33
+ message = translation.match('translation missing: (.+)') ? error.class.name : translation
34
+ render json: [message], status: error_status || 500, root: false
35
+ end
36
+ else
37
+ raise error
38
+ end
39
+ end
40
+
41
+ def render_turboboost_errors_for(record)
42
+ render json: record.errors.full_messages.to_a, status: :unprocessable_entity, root: false
43
+ end
44
+
45
+ def head_turboboost_success(turboboost_flash = {})
46
+ turboboost_flash = _turboboost_get_flash_messages(turboboost_flash)
47
+ head :ok, 'X-Flash' => turboboost_flash.to_json, 'X-Turboboosted' => '1'
48
+ end
49
+
50
+ def render(*args, &block)
51
+ if request.xhr? && request.headers['HTTP_X_TURBOBOOST']
52
+ turboboost_render(*args, &block)
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def turboboost_render(*args, &block)
59
+ options = _normalize_render(*args, &block)
60
+ [:replace, :within, :append, :prepend, :before, :after].each do |h|
61
+ response.headers["X-#{h.capitalize}"] = options[h] if options[h]
62
+ end
63
+ response.headers['X-Flash'] = _turboboost_get_flash_messages(options).to_json
64
+ response.headers['X-Turboboosted'] = '1'
65
+ self.response_body = render_to_body(options)
66
+ end
67
+
68
+ def redirect_to(options = {}, response_status_and_flash = {})
69
+ if request.xhr? && request.headers['HTTP_X_TURBOBOOST']
70
+ turboboost_redirect_to(options, response_status_and_flash)
71
+ else
72
+ super
73
+ end
74
+ end
75
+
76
+ def turboboost_redirect_to(options = {}, response_status_and_flash = {})
77
+ raise ActionControllerError.new('Cannot redirect to nil!') unless options
78
+ raise AbstractController::DoubleRenderError if response_body
79
+
80
+ # set flash for turbo redirect headers
81
+ turboboost_flash = _turboboost_get_flash_messages(response_status_and_flash)
82
+
83
+ if Rails.version < '4.2'
84
+ self.location = _compute_redirect_to_location(options)
85
+ else
86
+ self.location = _compute_redirect_to_location(request, options)
87
+ end
88
+
89
+ head :ok, 'X-Flash' => turboboost_flash.to_json
90
+
91
+ flash.update(turboboost_flash) # set flash for rendered view
92
+ end
93
+
94
+ def _turboboost_get_flash_messages(response_status_and_flash = {})
95
+ turboboost_flash = {}
96
+ flash_types = defined?(self.class._flash_types) ? self.class._flash_types : [:alert, :notice]
97
+ flash_types.each do |flash_type|
98
+ if type = flash.delete(type)
99
+ turboboost_flash.update(type)
100
+ end
101
+ if type = response_status_and_flash.delete(flash_type)
102
+ turboboost_flash[flash_type] = type
103
+ end
104
+ end
105
+ if other_flashes = response_status_and_flash.delete(:flash)
106
+ turboboost_flash.update(other_flashes)
107
+ end
108
+ turboboost_flash
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,46 @@
1
+ module Turboboost
2
+ # borrowed from - https://github.com/fs/turboforms/blob/master/lib/turboforms.rb
3
+ module FormHelper
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ alias_method_chain :form_for, :data_turboboost
8
+ alias_method_chain :form_tag, :data_turboboost
9
+ end
10
+
11
+ def form_for_with_data_turboboost(record_or_name_or_array, *args, &proc)
12
+ options = args.extract_options!
13
+
14
+ if options.key?(:turboboost) && options.delete(:turboboost)
15
+ options[:html] ||= {}
16
+ options[:html]['data-turboboost'] = true
17
+ options[:remote] = true
18
+ end
19
+
20
+ form_for_without_data_turboboost(record_or_name_or_array, *(args << options), &proc)
21
+ end
22
+
23
+ def form_tag_with_data_turboboost(record_or_name_or_array, *args, &proc)
24
+ options = args.extract_options!
25
+
26
+ if options.key?(:turboboost) && options.delete(:turboboost)
27
+ options[:data] ||= {}
28
+ options[:data]['turboboost'] = true
29
+ options[:remote] = true
30
+ end
31
+
32
+ form_tag_without_data_turboboost(record_or_name_or_array, *(args << options), &proc)
33
+ end
34
+
35
+ def convert_options_to_data_attributes(options, html_options)
36
+ if html_options
37
+ html_options = html_options.stringify_keys
38
+ if html_options.delete('turboboost')
39
+ html_options['data-remote'] = 'true'
40
+ html_options['data-turboboost'] = 'true'
41
+ end
42
+ end
43
+ super options, html_options
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module Turboboost
2
- VERSION = '0.0.11'
2
+ VERSION = '0.0.12'
3
3
  end
@@ -1,124 +1,87 @@
1
1
  require 'test_helper'
2
-
3
- class PostsController < ApplicationController
4
-
5
- respond_to :html
6
- respond_to :js, only: [:create, :update, :destroy]
7
-
8
- def create
9
- post = Post.create!(post_params)
10
- redirect_to posts_url, notice: "Post was successfully created."
11
- end
12
-
13
- def update
14
- post = Post.find(params[:id])
15
- post.update_attributes!(post_params)
16
- redirect_to post_url(post), notice: "Post was successfully updated."
17
- end
18
-
19
- def destroy
20
- post = Post.find(params[:id])
21
- post.destroy!
22
- redirect_to posts_url
23
- end
24
-
25
- private
26
-
27
- def post_params
28
- params.require(:post).permit(:title, :user_id)
29
- end
30
-
31
- end
32
-
33
- class UsersController < ApplicationController
34
-
35
- def create
36
- user = User.new(user_params)
37
- if user.save
38
- flash[:notice] = "User was successfully created."
39
- redirect_to user_url(user)
40
- else
41
- respond_to do |format|
42
- format.html { render :new }
43
- format.js { render_turboboost_errors_for(user) }
44
- end
45
- end
46
- end
47
-
48
- def update
49
- user = User.find(params[:id])
50
- if user.update_attributes(user_params)
51
- redirect_to user_url(user)
52
- else
53
- respond_to do |format|
54
- format.html { redirect_to users_url }
55
- format.js { render_turboboost_errors_for(user) }
56
- end
57
- end
58
- end
59
-
60
- private
61
-
62
- def user_params
63
- params.require(:user).permit(:name, :email)
64
- end
65
-
66
- end
2
+ Dir[File.dirname(__FILE__) + '/controllers/*.rb'].each { |file| require file }
67
3
 
68
4
  class PostsControllerTest < ActionController::TestCase
69
-
70
5
  tests PostsController
71
6
 
72
7
  setup do
73
- @request.headers["X-Turboboost"] = "1"
8
+ @request.headers['X-Turboboost'] = '1'
74
9
  end
75
10
 
76
- test "On a successful turboboost request, return an empty response with headers containing the redirect location and flash message" do
77
- xhr :post, :create, post: { title: "Foobar", user_id: "1" }
11
+ test 'On a successful turboboost request, return an empty response with headers containing the redirect location and flash message' do
12
+ xhr :post, :create, post: { title: 'Foobar', user_id: '1' }
78
13
 
79
14
  assert @response.body.strip.blank?
80
15
  assert_equal flash[:notice], 'Post was successfully created.'
81
- assert_equal @response.headers["Location"], posts_url
82
- assert_equal JSON.parse(@response.headers["X-Flash"])["notice"], 'Post was successfully created.'
16
+ assert_equal @response.headers['Location'], posts_url
17
+ assert_equal JSON.parse(@response.headers['X-Flash'])['notice'], 'Post was successfully created.'
83
18
  end
84
19
 
85
- test "On an unsuccessful turboboost request, catch and return the error message(s) as an array" do
86
- xhr :post, :create, post: { title: "Title", user_id: nil }
20
+ test 'On an unsuccessful turboboost request, catch and return the error message(s) as an array' do
21
+ xhr :post, :create, post: { title: 'Title', user_id: nil }
87
22
 
88
23
  assert_equal @response.status, 422
89
- assert_equal @response.body.strip, ["User can't be blank"].to_json
24
+ assert_equal @response.body.strip, ['User can\'t be blank'].to_json
90
25
 
91
- xhr :post, :create, post: { title: "Tit", user_id: nil }
26
+ xhr :post, :create, post: { title: 'Tit', user_id: nil }
92
27
 
93
28
  assert_equal @response.status, 422
94
- assert_equal @response.body.strip, ["Title is too short.", "User can't be blank"].to_json
29
+ assert_equal @response.body.strip, ['Title is too short.', "User can't be blank"].to_json
95
30
  end
96
-
97
31
  end
98
32
 
99
-
100
33
  class UsersControllerTest < ActionController::TestCase
101
-
102
34
  tests UsersController
103
35
 
104
36
  setup do
105
- @request.headers["X-Turboboost"] = "1"
37
+ @request.headers['X-Turboboost'] = '1'
106
38
  end
107
39
 
108
- test "On a successful turboboost request, return an empty response with headers containing the redirect location and flash message" do
109
- xhr :post, :create, user: { name: "Mike", email: "mike@mike.com" }
40
+ test 'On a successful turboboost request, return an empty response with headers containing the redirect location and flash message' do
41
+ xhr :post, :create, user: { name: 'Mike', email: 'mike@mike.com' }
110
42
 
111
- assert @response.body.strip.blank?
43
+ assert_equal @response.body.strip.blank?, true
112
44
  assert_equal flash[:notice], 'User was successfully created.'
113
- assert_equal @response.headers["Location"], user_url(1)
114
- assert_equal JSON.parse(@response.headers["X-Flash"])["notice"], 'User was successfully created.'
45
+ assert_equal @response.headers['Location'], user_url(1)
46
+ assert_equal JSON.parse(@response.headers['X-Flash'])['notice'], 'User was successfully created.'
115
47
  end
116
48
 
117
- test "On an unsuccessful turboboost request, explicitly render the error message(s)" do
118
- xhr :post, :create, user: { name: "Mike", email: "mike at mike.com" }
49
+ test 'On an unsuccessful turboboost request, explicitly render the error message(s)' do
50
+ xhr :post, :create, user: { name: 'Mike', email: 'mike at mike.com' }
119
51
 
120
52
  assert_equal @response.status, 422
121
- assert_equal @response.body.strip, ["Email is invalid"].to_json
53
+ assert_equal @response.body.strip, ['Email is invalid'].to_json
122
54
  end
55
+ end
123
56
 
57
+ class ItemsControllerTest < ActionController::TestCase
58
+ tests ItemsController
59
+
60
+ setup do
61
+ @request.headers['X-Turboboost'] = '1'
62
+ end
63
+
64
+ test 'On a failed turboboost get request, return custom internationalization messaging' do
65
+ xhr :get, :show, id: 123
66
+ i18n_message = I18n.t("turboboost.errors.ActiveRecord::RecordNotFound")
67
+ assert_equal @response.body.strip, [i18n_message].to_json
68
+ end
69
+
70
+ test 'On a successful turboboost post request, return rendering options in the headers' do
71
+ xhr :post, :create, item: { name: 'Bottle' }
72
+
73
+ assert_equal @response.headers['Location'], nil
74
+ assert_equal JSON.parse(@response.headers['X-Flash'])['notice'], 'Item was successfully created.'
75
+ assert_equal @response.headers['X-Within'], '#sidebar'
76
+ assert_equal @response.body.strip, "<div id=\"item\">Bottle</div>"
77
+ end
78
+
79
+ test 'On a successful turboboost update using render nothing: true, still return flash headers' do
80
+ @item = Item.create(name: 'Opener')
81
+ xhr :put, :update, id: @item.id, item: { name: 'Bottle Opener' }
82
+
83
+ assert_equal @response.headers['Location'], nil
84
+ assert_equal @response.body.strip, ''
85
+ assert_equal JSON.parse(@response.headers['X-Flash'])['notice'], 'Item updated.'
86
+ end
124
87
  end
@@ -0,0 +1,23 @@
1
+ class ItemsController < ApplicationController
2
+ def show
3
+ item = Item.find(params[:id])
4
+ render json: item
5
+ end
6
+
7
+ def create
8
+ @item = Item.create!(item_params)
9
+ render :show, within: '#sidebar', flash: { notice: 'Item was successfully created.' }
10
+ end
11
+
12
+ def update
13
+ item = Item.find(params[:id])
14
+ item.update_attributes!(item_params)
15
+ render nothing: true, notice: 'Item updated.'
16
+ end
17
+
18
+ private
19
+
20
+ def item_params
21
+ params.require(:item).permit(:name)
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ class PostsController < ApplicationController
2
+ respond_to :html
3
+ respond_to :js, only: [:create, :update, :destroy]
4
+
5
+ def create
6
+ Post.create!(post_params)
7
+ redirect_to posts_url, notice: 'Post was successfully created.'
8
+ end
9
+
10
+ def update
11
+ post = Post.find(params[:id])
12
+ post.update_attributes!(post_params)
13
+ redirect_to post_url(post), notice: 'Post was successfully updated.'
14
+ end
15
+
16
+ def destroy
17
+ post = Post.find(params[:id])
18
+ post.destroy!
19
+ redirect_to posts_url
20
+ end
21
+
22
+ private
23
+
24
+ def post_params
25
+ params.require(:post).permit(:title, :user_id)
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ class UsersController < ApplicationController
2
+ def create
3
+ user = User.new(user_params)
4
+ if user.save
5
+ flash[:notice] = 'User was successfully created.'
6
+ redirect_to user_url(user)
7
+ else
8
+ respond_to do |format|
9
+ format.html { render :new }
10
+ format.js { render_turboboost_errors_for(user) }
11
+ end
12
+ end
13
+ end
14
+
15
+ def update
16
+ user = User.find(params[:id])
17
+ if user.update_attributes(user_params)
18
+ redirect_to user_url(user)
19
+ else
20
+ respond_to do |format|
21
+ format.html { redirect_to users_url }
22
+ format.js { render_turboboost_errors_for(user) }
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def user_params
30
+ params.require(:user).permit(:name, :email)
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ en:
2
+ turboboost:
3
+ errors:
4
+ "ActiveRecord::RecordNotFound": "Shoot, didn't find anything."
data/test/test_helper.rb CHANGED
@@ -2,31 +2,39 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  Bundler.setup
4
4
 
5
- ENV["RAILS_ENV"] = "test"
5
+ ENV['RAILS_ENV'] = 'test'
6
6
 
7
7
  require 'rails'
8
- require "active_record"
9
- require "action_controller"
10
- require "action_controller"
11
- require "action_controller/railtie"
8
+ require 'active_record'
9
+ require 'action_controller'
10
+ require 'action_controller'
11
+ require 'action_controller/railtie'
12
12
  require 'rails/test_help'
13
13
  require 'awesome_print'
14
+ require 'responders' if Rails.version >= '4.2'
15
+ require 'strong_parameters' if Rails.version < '4.0'
14
16
  require 'turboboost'
15
17
 
16
- ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
18
+ I18n.enforce_available_locales = true
19
+ I18n.load_path << File.expand_path('../locales/en.yml', __FILE__)
20
+ I18n.reload!
21
+
22
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
17
23
 
18
24
  class TestApp < Rails::Application; end
19
25
  Rails.application = TestApp
20
- Rails.configuration.secret_key_base = "abc123"
26
+ Rails.configuration.secret_key_base = 'abc123'
21
27
 
22
28
  Turboboost::Routes = ActionDispatch::Routing::RouteSet.new
23
29
  Turboboost::Routes.draw do
24
30
  resources 'posts'
25
31
  resources 'users'
32
+ resources 'items'
26
33
  end
27
34
 
28
35
  class ApplicationController < ActionController::Base
29
36
  include Turboboost::Routes.url_helpers
37
+ self.view_paths = File.join(File.dirname(__FILE__), 'views')
30
38
  end
31
39
 
32
40
  class ActiveSupport::TestCase
@@ -36,38 +44,42 @@ class ActiveSupport::TestCase
36
44
  end
37
45
 
38
46
  class ActionView::TestCase
39
-
40
- def default_url_options; {}; end
41
-
42
47
  include Turboboost::Routes.url_helpers
43
48
  include Turboboost::FormHelper
49
+ def default_url_options
50
+ {}
51
+ end
44
52
 
45
53
  setup do
46
54
  @controller = ApplicationController
47
55
  end
48
-
49
56
  end
50
57
 
51
58
  posts_table = %{CREATE TABLE posts (id INTEGER PRIMARY KEY, title VARCHAR(5), user_id INTEGER);}
52
59
  ActiveRecord::Base.connection.execute(posts_table)
53
60
 
54
61
  class Post < ActiveRecord::Base
55
-
56
62
  attr_accessor :title, :user_id
57
63
 
58
- validates :title, length: { minimum: 5, message: "is too short." }
64
+ validates :title, length: { minimum: 5, message: 'is too short.' }
59
65
  validates :user_id, presence: true
60
-
61
66
  end
62
67
 
63
68
  users_table = %{CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR(5), email VARCHAR(255));}
64
69
  ActiveRecord::Base.connection.execute(users_table)
65
70
 
66
71
  class User < ActiveRecord::Base
67
-
68
72
  attr_accessor :name, :email
69
73
 
70
74
  validates :name, presence: true
71
75
  validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, allow_blank: false }
76
+ end
72
77
 
78
+ items_table = %{CREATE TABLE items (id INTEGER PRIMARY KEY, name VARCHAR(5));}
79
+ ActiveRecord::Base.connection.execute(items_table)
80
+
81
+ class Item < ActiveRecord::Base
82
+ attr_accessor :name
83
+
84
+ validates :name, presence: true
73
85
  end