tuning 0.3.3 → 1.0.0

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.
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