tuning 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +80 -17
  4. data/Rakefile +3 -0
  5. data/lib/tuning.rb +7 -2
  6. data/lib/tuning/extensions/action_controller/base.rb +45 -0
  7. data/lib/tuning/extensions/action_mailer/base.rb +22 -0
  8. data/lib/tuning/extensions/action_view/base.rb +31 -0
  9. data/lib/tuning/extensions/active_record/base.rb +25 -0
  10. data/lib/tuning/locales/en.yml +12 -0
  11. data/lib/tuning/locales/es.yml +12 -0
  12. data/lib/tuning/railtie.rb +17 -3
  13. data/lib/tuning/validations/complexity.rb +22 -0
  14. data/lib/tuning/validations/count.rb +42 -0
  15. data/lib/tuning/validations/time.rb +38 -0
  16. data/lib/tuning/version.rb +1 -1
  17. data/test/controllers_test.rb +21 -0
  18. data/test/dummy/Rakefile +1 -2
  19. data/test/dummy/app/assets/javascripts/application.js +2 -2
  20. data/test/dummy/app/assets/stylesheets/application.css +6 -4
  21. data/test/dummy/app/mailers/user_mailer.rb +7 -0
  22. data/test/dummy/app/models/shop.rb +2 -0
  23. data/test/dummy/app/views/layouts/application.html.erb +0 -12
  24. data/test/dummy/app/views/layouts/market.html.erb +3 -0
  25. data/test/dummy/app/views/user_mailer/invite.text.erb +7 -0
  26. data/test/dummy/bin/bundle +0 -0
  27. data/test/dummy/bin/rails +1 -1
  28. data/test/dummy/bin/rake +0 -0
  29. data/test/dummy/bin/setup +29 -0
  30. data/test/dummy/config.ru +1 -1
  31. data/test/dummy/config/application.rb +3 -1
  32. data/test/dummy/config/boot.rb +1 -1
  33. data/test/dummy/config/database.yml +4 -22
  34. data/test/dummy/config/database.yml.travis +12 -0
  35. data/test/dummy/config/environment.rb +1 -1
  36. data/test/dummy/config/environments/development.rb +14 -2
  37. data/test/dummy/config/environments/production.rb +20 -25
  38. data/test/dummy/config/environments/test.rb +8 -10
  39. data/test/dummy/config/initializers/assets.rb +11 -0
  40. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  41. data/test/dummy/config/initializers/mime_types.rb +1 -2
  42. data/test/dummy/config/initializers/session_store.rb +1 -1
  43. data/test/dummy/config/locales/en.yml +22 -9
  44. data/test/dummy/config/routes.rb +2 -7
  45. data/test/dummy/config/secrets.yml +22 -0
  46. data/test/dummy/db/migrate/20161020200126_create_shops.rb +9 -0
  47. data/test/dummy/db/migrate/20161022144717_create_records.rb +6 -0
  48. data/test/dummy/db/schema.rb +15 -1
  49. data/test/dummy/log/test.log +70 -0
  50. data/test/dummy/public/404.html +20 -11
  51. data/test/dummy/public/422.html +20 -11
  52. data/test/dummy/public/500.html +19 -10
  53. data/test/helpers_test.rb +39 -0
  54. data/test/mailers_test.rb +10 -0
  55. data/test/records_test.rb +16 -0
  56. data/test/test_helper.rb +5 -18
  57. data/test/validations_test.rb +192 -0
  58. metadata +66 -47
  59. data/lib/tuning/action_controller/base.rb +0 -54
  60. data/lib/tuning/action_view/base.rb +0 -16
  61. data/test/active_trail_test.rb +0 -33
  62. data/test/content_tag_if_test.rb +0 -17
  63. data/test/dummy/README.rdoc +0 -28
  64. data/test/dummy/app/controllers/status_controller.rb +0 -27
  65. data/test/dummy/config/initializers/secret_token.rb +0 -13
  66. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  72. data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  73. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  74. data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  75. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  76. data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  77. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  78. data/test/status_test.rb +0 -55
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18821f4d89a8574296569885f2551ec2af4d9940
4
- data.tar.gz: 2ff074c48720859869d3b1ab9dbdbdfa410c1f48
3
+ metadata.gz: 343b848737c55c9904274280fa22d39825774cd6
4
+ data.tar.gz: d958a82c6f8109cc0349978d59bbc23fda0ce85c
5
5
  SHA512:
6
- metadata.gz: 6c365fc74873b685e28a083955554c89e5d8a0541c19186dafd0e51a593a9be0abfbc3da312acc1aaed6373c1aab50f5f7e88786840f4cb05433be88782aed2f
7
- data.tar.gz: a4b6237569666ae1fd478eb5f08e6a2b853ba579d37eae5f25c31a2660d9586981d8646d85240f4ebaaf3b732f434fb6f8425e8dcbd94d40f7ac1b3a326e2de9
6
+ metadata.gz: 49d74ede892d4044c00902cc3ddd33cb405fa79b00406e3ad975a3b2b1eb0b72337ad13fad9899580da53b4e9983b25562b8be877abc49bbded7c8fb4a651f1a
7
+ data.tar.gz: 3641e84db0a2bc40ab09def6907be2b3a60e6efccd3c2ede3de92f620583f1eaad6655efec327171623f5767c81867807bc035e64a23cf48f95556335130f0dc
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2015 Mathías Montossi
1
+ Copyright 2016 Mathías Montossi
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -21,45 +21,108 @@ $ bundle
21
21
 
22
22
  ## Controllers
23
23
 
24
- Use error method to respond with status 500 and show 500.html (if format it's html):
24
+ New callbacks before, after, around render are available:
25
25
  ```ruby
26
- error
27
- ```
26
+ class ProductsController < ApplicationController
28
27
 
29
- Use not_found to respond with status 404 and show 404.html (if format it's html):
30
- ```ruby
31
- not_found
28
+ before_action :set_product
29
+ before_render :prepare_product
30
+
31
+ def edit
32
+ end
33
+
34
+ private
35
+
36
+ def set_product
37
+ @product = Product.find(params[:id])
38
+ end
39
+
40
+ def prepare_product
41
+ @product.build_picture
42
+ end
43
+
44
+ end
32
45
  ```
33
46
 
34
- Use unauthorized to respond with status 401 and show 422.html (if format it's html):
35
- ```ruby
36
- unauthorized
47
+ ## Mailers
48
+
49
+ Text email templates will normalize spaces and new lines like html:
50
+ ```erb
51
+ <% if @order.confirmed? %>
52
+ Your order has been confirmed.
53
+
54
+ Will be delivered right the way.
55
+ <% end %>
37
56
  ```
38
57
 
39
- Use forbidden to respond with status 403 and show 422.html (if format it's html):
40
- ```ruby
41
- forbidden
58
+ Will produce:
42
59
  ```
60
+ Your order has been confirmed.
43
61
 
44
- Use unprocessable_entity to respond with status 422 and show 422.html (if format it's html):
45
- ```ruby
46
- unprocessable_entity
62
+ Will be delivered right the way.
47
63
  ```
48
64
 
49
65
  ## Views
50
66
 
51
- Use content_tag_if if you want wrap content into some tag if certain condition it's true:
67
+ New content_tag_if method to wrap content into some tag if certain condition it's true:
52
68
  ```erb
53
69
  <%= content_tag_if request.path == home_path, :h1 do %>
54
70
  <%= link_to 'Home', home_path, id: 'logo' %>
55
71
  <% end %>
56
72
  ```
57
73
 
58
- Use active_trail? if you want to check if some path is on active trail:
74
+ New active_trail? method to check if some path is on active trail:
59
75
  ```erb
60
76
  <li class="<%= 'active' if active_trail? some_path %>"></li>
61
77
  ```
62
78
 
79
+ New extending method to extend layouts:
80
+ ```erb
81
+ <%= extending :application do %>
82
+ <p>content</p>
83
+ <% end %>
84
+ ```
85
+
86
+ ## Records
87
+
88
+ Empty strings will be nilify in the database to avoid sql errors or complex queries:
89
+ ```ruby
90
+ shop = Shop.new(name: '')
91
+ shop.save
92
+ shop.name # Will be nil
93
+ ```
94
+
95
+ New method validate is available to allow a more expressive syntax:
96
+ ```ruby
97
+ record.validate # Same as valid?
98
+ ```
99
+
100
+ ## Validations
101
+
102
+ Complexity validator to avoid weak passwords:
103
+ ```ruby
104
+ class User < ActiveRecord::Base
105
+ validates_complexity_of :password
106
+ end
107
+ ```
108
+
109
+ Count validator to express count messages instead of length messages:
110
+ ```ruby
111
+ class Product < ActiveRecord::Base
112
+ validates_count_of :pictures, minimum: 1, maximum: 4 # Or in/within: 1..4
113
+ end
114
+ ```
115
+
116
+ Time validator to validate Date/Time values:
117
+ ```ruby
118
+ class Schedule < ActiveRecord::Base
119
+ validates_time_of :opens_at
120
+ validates_time_of :closes_at, after: :opens_at # Or before, after_or_equal_to or before_or_equal_to
121
+ end
122
+ ```
123
+
124
+ NOTE: Take a look at lib/tuning/locales to know the i18n keys.
125
+
63
126
  ## Credits
64
127
 
65
128
  This gem is maintained and funded by [mmontossi](https://github.com/mmontossi).
data/Rakefile CHANGED
@@ -19,6 +19,9 @@ end
19
19
 
20
20
  Bundler::GemHelper.install_tasks
21
21
 
22
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
23
+ load 'rails/tasks/engine.rake'
24
+
22
25
  require 'rake/testtask'
23
26
 
24
27
  Rake::TestTask.new(:test) do |t|
data/lib/tuning.rb CHANGED
@@ -1,5 +1,10 @@
1
- require 'tuning/action_controller/base'
2
- require 'tuning/action_view/base'
1
+ require 'tuning/extensions/action_controller/base'
2
+ require 'tuning/extensions/action_mailer/base'
3
+ require 'tuning/extensions/action_view/base'
4
+ require 'tuning/extensions/active_record/base'
5
+ require 'tuning/validations/complexity'
6
+ require 'tuning/validations/count'
7
+ require 'tuning/validations/time'
3
8
  require 'tuning/railtie'
4
9
 
5
10
  module Tuning
@@ -0,0 +1,45 @@
1
+ module Tuning
2
+ module Extensions
3
+ module ActionController
4
+ module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ define_callbacks :render
9
+ end
10
+
11
+ def render(*args)
12
+ run_callbacks(:render) do
13
+ super
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ %i(before after around).each do |callback|
20
+ define_method "#{callback}_render" do |*names, &block|
21
+ _insert_callbacks(args, block) do |name, options|
22
+ set_callback :render, callback, name, options
23
+ end
24
+ end
25
+
26
+ define_method "prepend_#{callback}_render" do |*names, &block|
27
+ _insert_callbacks(args, block) do |name, options|
28
+ set_callback :render, callback, name, options.merge(prepend: true)
29
+ end
30
+ end
31
+
32
+ define_method "skip_#{callback}_render" do |*names|
33
+ _insert_callbacks(args) do |name, options|
34
+ skip_callback :render, callback, name, options
35
+ end
36
+ end
37
+
38
+ alias_method :"append_#{callback}_render", :"#{callback}_render"
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Tuning
2
+ module Extensions
3
+ module ActionMailer
4
+ module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_action :normalize_text_body
9
+ end
10
+
11
+ private
12
+
13
+ def normalize_text_body
14
+ if mail.content_type.starts_with?('text/plain')
15
+ mail.body = mail.body.to_s.gsub(/\n{3,}/, "\n\n").gsub(/\n +/, "\n").gsub(/^ +/, '')
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ module Tuning
2
+ module Extensions
3
+ module ActionView
4
+ module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ def active_trail?(path)
8
+ (path == '/' && request.path == path) || request.path.start_with?(path)
9
+ end
10
+
11
+ def content_tag_if(condition, name, options={}, &block)
12
+ if condition
13
+ content_tag(name, options, &block)
14
+ else
15
+ capture(&block)
16
+ end
17
+ end
18
+
19
+ def extending(layout, &block)
20
+ if block_given?
21
+ old_layout = @view_flow.get(:layout)
22
+ new_layout = (capture(&block) || '')
23
+ old_layout.replace(new_layout)
24
+ end
25
+ render file: "layouts/#{layout}"
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module Tuning
2
+ module Extensions
3
+ module ActiveRecord
4
+ module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_save :nilify_blank_attributes
9
+ alias_method :validate, :valid?
10
+ end
11
+
12
+ private
13
+
14
+ def nilify_blank_attributes
15
+ attributes.each do |column, value|
16
+ if value.is_a? String and value.blank?
17
+ self[column] = nil
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,12 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ too_easy: "is too easy"
5
+ too_many: "must have maximum %{count}"
6
+ too_few: "must have minimum %{count}"
7
+ wrong_count: "must have %{count}"
8
+ not_a_time: "is not a time"
9
+ after: "must be after %{time}"
10
+ after_or_equal_to: "must be after or equal to %{time}"
11
+ before: "must be before %{time}"
12
+ before_or_equal_to: "must be before or equal to %{time}"
@@ -0,0 +1,12 @@
1
+ es:
2
+ errors:
3
+ messages:
4
+ too_easy: "es muy fácil"
5
+ too_many: "debe tener máximo %{count}"
6
+ too_few: "debe tener mínimo %{count}"
7
+ wrong_count: "debe tener %{count}"
8
+ not_a_time: "no es un tiempo"
9
+ after: "debe ser posterior a %{time}"
10
+ after_or_equal_to: "tiene que ser posterior o igual a %{time}"
11
+ before: "debe ser anterior a %{time}"
12
+ before_or_equal_to: "tiene que ser anterior o igual a %{time}"
@@ -1,9 +1,23 @@
1
1
  module Tuning
2
2
  class Railtie < Rails::Railtie
3
3
 
4
- initializer 'tuning' do
5
- ::ActionController::Base.send :include, Tuning::ActionController::Base
6
- ::ActionView::Base.send :include, Tuning::ActionView::Base
4
+ initializer :tuning do
5
+ ::ActionController::Base.include(
6
+ Tuning::Extensions::ActionController::Base
7
+ )
8
+ ::ActionMailer::Base.include(
9
+ Tuning::Extensions::ActionMailer::Base
10
+ )
11
+ ::ActionView::Base.include(
12
+ Tuning::Extensions::ActionView::Base
13
+ )
14
+ ::ActiveRecord::Base.include(
15
+ Tuning::Extensions::ActiveRecord::Base,
16
+ Tuning::Validations
17
+ )
18
+ ::ActiveModel::Model.include(
19
+ Tuning::Validations
20
+ )
7
21
  end
8
22
 
9
23
  end
@@ -0,0 +1,22 @@
1
+ module Tuning
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ class ComplexityValidator < ActiveModel::EachValidator
6
+
7
+ def validate_each(record, attribute, value)
8
+ if value !~ /\A(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[\W\_]).{8,}*\z/
9
+ record.errors.add attribute, :too_easy
10
+ end
11
+ end
12
+
13
+ end
14
+ module ClassMethods
15
+
16
+ def validates_complexity_of(*attr_names)
17
+ validates_with ComplexityValidator, _merge_attributes(attr_names)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ module Tuning
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ class CountValidator < ActiveModel::EachValidator
6
+ MESSAGES = { is: :wrong_count, minimum: :too_few, maximum: :too_many }
7
+ CHECKS = { is: :==, minimum: :>=, maximum: :<= }
8
+
9
+ def initialize(options)
10
+ if range = (options[:in] || options[:within])
11
+ options[:minimum] = range.min
12
+ options[:maximum] = range.max
13
+ end
14
+ super
15
+ end
16
+
17
+ def validate_each(record, attribute, value)
18
+ if value.respond_to?(:size)
19
+ count = value.size
20
+ CHECKS.each do |name, operator|
21
+ if options.has_key?(name)
22
+ other = options[name]
23
+ unless count.send(operator, other)
24
+ record.errors.add attribute, MESSAGES[name], count: other
25
+ end
26
+ end
27
+ end
28
+ else
29
+ record.errors.add attribute, :uncountable
30
+ end
31
+ end
32
+
33
+ end
34
+ module ClassMethods
35
+
36
+ def validates_count_of(*attr_names)
37
+ validates_with CountValidator, _merge_attributes(attr_names)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ module Tuning
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ class TimeValidator < ActiveModel::EachValidator
6
+ CHECKS = { before: :<, before_or_equal_to: :<=, after: :>, after_or_equal_to: :>= }
7
+
8
+ def validate_each(record, attribute, value)
9
+ if [Date, Time].any? { |klass| value.kind_of?(klass) }
10
+ CHECKS.each do |name, operator|
11
+ if options.has_key?(name)
12
+ other = options[name]
13
+ case other
14
+ when Symbol
15
+ other = record.send(other)
16
+ when Proc
17
+ other = other.call(record)
18
+ end
19
+ unless value.send(operator, other)
20
+ record.errors.add attribute, name, time: I18n.l(other)
21
+ end
22
+ end
23
+ end
24
+ else
25
+ record.errors.add attribute, :not_a_time
26
+ end
27
+ end
28
+
29
+ end
30
+ module ClassMethods
31
+
32
+ def validates_time_of(*attr_names)
33
+ validates_with TimeValidator, _merge_attributes(attr_names)
34
+ end
35
+
36
+ end
37
+ end
38
+ end