turboboost 0.0.11 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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