eac_rails_utils 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +18 -0
- data/lib/assets/javascripts/currency_field.js +21 -0
- data/lib/assets/javascripts/eac_rails_utils.js +6 -0
- data/lib/assets/javascripts/input_searchable.js +78 -0
- data/lib/assets/javascripts/jMenu.jquery.min.js +12 -0
- data/lib/assets/javascripts/jquery.maskMoney.js +409 -0
- data/lib/assets/stylesheets/autocomplete.scss +33 -0
- data/lib/assets/stylesheets/bootstrap3-dropdown.scss +47 -0
- data/lib/assets/stylesheets/eac_rails_utils.scss +44 -0
- data/lib/assets/stylesheets/jmenu.scss +78 -0
- data/lib/eac/common_form_helper.rb +37 -0
- data/lib/eac/common_form_helper/form_builder.rb +88 -0
- data/lib/eac/common_form_helper/form_builder/association_select_field.rb +40 -0
- data/lib/eac/common_form_helper/form_builder/common_text_fields.rb +18 -0
- data/lib/eac/common_form_helper/form_builder/currency_field.rb +39 -0
- data/lib/eac/common_form_helper/form_builder/date_field.rb +20 -0
- data/lib/eac/common_form_helper/form_builder/fields_for.rb +44 -0
- data/lib/eac/common_form_helper/form_builder/radio_select_field.rb +44 -0
- data/lib/eac/common_form_helper/form_builder/searchable_association_field.rb +77 -0
- data/lib/eac/cpf_validator.rb +60 -0
- data/lib/eac/formatter_helper.rb +27 -0
- data/lib/eac/htmlbeautifier.rb +25 -0
- data/lib/eac/menus_helper.rb +17 -0
- data/lib/eac/menus_helper/bootstrap_gui_builder.rb +90 -0
- data/lib/eac/menus_helper/data_builder.rb +57 -0
- data/lib/eac/menus_helper/gui_builder.rb +51 -0
- data/lib/eac/model.rb +87 -0
- data/lib/eac/simple_cache.rb +29 -0
- data/lib/eac/source_target_fixtures.rb +51 -0
- data/lib/eac_rails_utils.rb +29 -0
- data/lib/eac_rails_utils/patches/model_attribute_required.rb +31 -0
- data/lib/eac_rails_utils/rails/engine.rb +5 -0
- data/lib/eac_rails_utils/version.rb +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/models/job.rb +4 -0
- data/test/dummy/app/models/user.rb +7 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +12 -0
- data/test/dummy/config/boot.rb +4 -0
- data/test/dummy/config/database.yml +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/routes.rb +8 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/migrate/20160415125333_create_users.rb +9 -0
- data/test/dummy/db/migrate/20160415143123_create_jobs.rb +7 -0
- data/test/dummy/db/migrate/20160415143229_add_job_to_users.rb +5 -0
- data/test/dummy/db/schema.rb +25 -0
- data/test/lib/eac/common_form_helper_test.rb +31 -0
- data/test/lib/eac/cpf_validator_test.rb +33 -0
- data/test/lib/eac/formatter_helper_test.rb +19 -0
- data/test/lib/eac/model_test.rb +76 -0
- data/test/lib/eac/simple_cache_test.rb +56 -0
- data/test/lib/eac_rails_utils/patches/model_attribute_required_test.rb +39 -0
- data/test/test_helper.rb +11 -0
- metadata +189 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Eac
|
2
|
+
module CommonFormHelper
|
3
|
+
class FormBuilder
|
4
|
+
module FieldsFor
|
5
|
+
def fields_for(association, &block)
|
6
|
+
fieldset(association) do
|
7
|
+
nested_fields(association, &block) << add_link(association)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def nested_fields(association, &block)
|
14
|
+
form.nested_fields_for(association) do |nested_form|
|
15
|
+
helper.content_tag(:div, class: 'nested_form_row') do
|
16
|
+
helper.capture(FormBuilder.new(nested_form, helper), &block) <<
|
17
|
+
remove_link(nested_form)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_link(association)
|
23
|
+
form.add_nested_fields_link(association, 'Adicionar', class: 'btn btn-primary',
|
24
|
+
role: 'button')
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove_link(nested_form)
|
28
|
+
nested_form.remove_nested_fields_link('Remover', class: 'btn btn-danger',
|
29
|
+
role: 'button')
|
30
|
+
end
|
31
|
+
|
32
|
+
def fieldset(association, &block)
|
33
|
+
helper.content_tag(:fieldset, class: 'nested_form') do
|
34
|
+
helper.content_tag(:legend, fieldset_legend(association)) << block.call
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def fieldset_legend(association)
|
39
|
+
I18n.t("helpers.label.#{form.object.class.name.underscore}.#{association}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Eac
|
2
|
+
module CommonFormHelper
|
3
|
+
class FormBuilder
|
4
|
+
module RadioSelectField
|
5
|
+
def radio_select_field(field_name, radios_values, options = {})
|
6
|
+
field(field_name, options) do
|
7
|
+
radios(field_name, radios_values)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def radios(field_name, radios_values)
|
14
|
+
b = ActiveSupport::SafeBuffer.new
|
15
|
+
radios_values.each do |r|
|
16
|
+
b << radio(field_name, r[0], r[1])
|
17
|
+
end
|
18
|
+
b
|
19
|
+
end
|
20
|
+
|
21
|
+
def radio(field_name, value, label)
|
22
|
+
helper.content_tag(:div, class: 'radio') do
|
23
|
+
helper.content_tag(:label) do
|
24
|
+
form.radio_button(field_name, value) << label
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract_select_options(options)
|
30
|
+
options.extract!(:prompt, :include_blank)
|
31
|
+
end
|
32
|
+
|
33
|
+
def extract_association_key(field_name, options, key, method)
|
34
|
+
return options.delete(key) if options.key?(key)
|
35
|
+
if model_instance.class.respond_to?(:reflect_on_association)
|
36
|
+
return model_instance.class.reflect_on_association(field_name).send(method)
|
37
|
+
end
|
38
|
+
fail "#{model_instance.class} não possui um método \"reflect_on_association\". " \
|
39
|
+
"Defina explicitamente a opção :#{key}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Eac
|
2
|
+
module CommonFormHelper
|
3
|
+
class FormBuilder
|
4
|
+
class SearchableAssociationField
|
5
|
+
def initialize(form_builder, field_name, options)
|
6
|
+
@form_builder = form_builder
|
7
|
+
@field_name = field_name
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def hidden_input
|
12
|
+
@form_builder.form.hidden_field(hidden_input_name, id: hidden_input_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def visible_input
|
16
|
+
@form_builder.helper.text_field_tag(visible_input_name, '', id: visible_input_id,
|
17
|
+
class: 'form-control')
|
18
|
+
end
|
19
|
+
|
20
|
+
def javascript_tag
|
21
|
+
@form_builder.helper.content_tag(:script) do
|
22
|
+
@form_builder.helper.raw("new InputSearchable(#{json_options});")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def hidden_input_id
|
29
|
+
@hidden_input_id ||= SecureRandom.hex(5)
|
30
|
+
end
|
31
|
+
|
32
|
+
def hidden_input_name
|
33
|
+
"#{@field_name}_id"
|
34
|
+
end
|
35
|
+
|
36
|
+
def visible_input_id
|
37
|
+
@visible_input_id ||= SecureRandom.hex(5)
|
38
|
+
end
|
39
|
+
|
40
|
+
def visible_input_name
|
41
|
+
"#{@field_name}_search"
|
42
|
+
end
|
43
|
+
|
44
|
+
def association_column
|
45
|
+
@form_builder.model_instance && @form_builder.model_instance.send(@field_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def initial_id
|
49
|
+
return association_column.id if association_column
|
50
|
+
''
|
51
|
+
end
|
52
|
+
|
53
|
+
def initial_label
|
54
|
+
return association_column.to_s if association_column
|
55
|
+
return params[visible_input_name] if params.key?(visible_input_name)
|
56
|
+
''
|
57
|
+
end
|
58
|
+
|
59
|
+
def params
|
60
|
+
@form_builder.helper.params
|
61
|
+
end
|
62
|
+
|
63
|
+
def url
|
64
|
+
@options[:url]
|
65
|
+
end
|
66
|
+
|
67
|
+
def json_options
|
68
|
+
r = {}
|
69
|
+
[:hidden_input_id, :visible_input_id, :initial_id, :initial_label, :url].each do |k|
|
70
|
+
r[k] = send(k)
|
71
|
+
end
|
72
|
+
r.to_json
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Eac
|
2
|
+
class CpfValidator < ActiveModel::EachValidator
|
3
|
+
def validate_each(record, attribute, value)
|
4
|
+
return if Cpf.new(value).valid?
|
5
|
+
record.errors[attribute] << (options[:message] ||
|
6
|
+
'CPF inválido (9 caracteres, somente dígitos)')
|
7
|
+
end
|
8
|
+
|
9
|
+
class Cpf
|
10
|
+
def initialize(input)
|
11
|
+
@input = input
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
return false if input_invalid?
|
16
|
+
digito_verificador1_calculo == values[9] && digito_verificador2_calculo == values[10]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :input
|
22
|
+
|
23
|
+
def input_invalid?
|
24
|
+
input.nil? || digits.length != 11 || digits.length != input.length || null?
|
25
|
+
end
|
26
|
+
|
27
|
+
def null?
|
28
|
+
%w(12345678909 11111111111 22222222222 33333333333 44444444444 55555555555
|
29
|
+
66666666666 77777777777 88888888888 99999999999 00000000000).member?(digits.join)
|
30
|
+
end
|
31
|
+
|
32
|
+
def digits
|
33
|
+
@digits ||= input.scan(/[0-9]/)
|
34
|
+
end
|
35
|
+
|
36
|
+
def values
|
37
|
+
@values ||= digits.collect(&:to_i)
|
38
|
+
end
|
39
|
+
|
40
|
+
def digito_verificador1_calculo
|
41
|
+
digito_verificador_calculo(9, 10)
|
42
|
+
end
|
43
|
+
|
44
|
+
def digito_verificador2_calculo
|
45
|
+
digito_verificador_calculo(10, 11)
|
46
|
+
end
|
47
|
+
|
48
|
+
def digito_verificador_calculo(valores_count, coeficiente_inicial)
|
49
|
+
s = 0
|
50
|
+
c = coeficiente_inicial
|
51
|
+
(0..valores_count - 1).each do |i|
|
52
|
+
s += values[i] * c
|
53
|
+
c -= 1
|
54
|
+
end
|
55
|
+
s -= (11 * (s / 11))
|
56
|
+
(s == 0 || s == 1) ? 0 : 11 - s
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Eac
|
2
|
+
module FormatterHelper
|
3
|
+
include ActionView::Helpers::NumberHelper
|
4
|
+
|
5
|
+
def format_real(value)
|
6
|
+
number_to_currency(
|
7
|
+
value,
|
8
|
+
unit: 'R$ ',
|
9
|
+
separator: ',',
|
10
|
+
delimiter: '.',
|
11
|
+
raise: true
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def format_percentage(float_value)
|
16
|
+
number_to_percentage(float_value * 100, precision: 0)
|
17
|
+
end
|
18
|
+
|
19
|
+
def brl_currency_to_float(currency)
|
20
|
+
currency.to_s.gsub(/[R$ .]/, '').tr(',', '.').to_f
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_cep(cep)
|
24
|
+
"#{cep[0, 5]}-#{cep[5, 3]}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'htmlbeautifier'
|
2
|
+
|
3
|
+
module Eac
|
4
|
+
class Htmlbeautifier
|
5
|
+
def self.beautify(string)
|
6
|
+
::HtmlBeautifier.beautify(string, tab_stops: 2) + "\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.file_beautified?(file)
|
10
|
+
input = File.read(file)
|
11
|
+
input == beautify(input)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.beautify_file(file)
|
15
|
+
input = File.read(file)
|
16
|
+
output = beautify(input)
|
17
|
+
if input == output
|
18
|
+
false
|
19
|
+
else
|
20
|
+
File.write(file, output)
|
21
|
+
true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Eac
|
2
|
+
module MenusHelper
|
3
|
+
def dropdown_menu(entries)
|
4
|
+
entries = DataBuilder.new(self).build(entries)
|
5
|
+
return nil unless entries
|
6
|
+
id = SecureRandom.hex(5)
|
7
|
+
GuiBuilder.new(self).build(entries, id: id, class: 'jMenu') <<
|
8
|
+
content_tag(:script) { raw("$(document).ready(function(){$('\##{id}').jMenu();});") }
|
9
|
+
end
|
10
|
+
|
11
|
+
def bootstrap_dropdown_menu(entries, options = {})
|
12
|
+
entries = DataBuilder.new(self).build(entries)
|
13
|
+
return nil unless entries
|
14
|
+
BootstrapGuiBuilder.new(self, options).build(entries)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Eac
|
2
|
+
module MenusHelper
|
3
|
+
# http://bootsnipp.com/snippets/featured/multi-level-dropdown-menu-bs3
|
4
|
+
class BootstrapGuiBuilder
|
5
|
+
def initialize(view, options = {})
|
6
|
+
@view = view
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def build(entries)
|
11
|
+
fail "Argument \"entries\" is not a array" unless entries.is_a?(Array)
|
12
|
+
@view.content_tag(:ul, class: "nav navbar-nav #{@options[:class]}".strip) do
|
13
|
+
menu_entries(entries)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def menu_entries(entries)
|
20
|
+
b = ActiveSupport::SafeBuffer.new
|
21
|
+
entries.map { |v| b << menu_entry(v) }
|
22
|
+
b
|
23
|
+
end
|
24
|
+
|
25
|
+
def menu_entry(entry)
|
26
|
+
if entry[:type] == :group
|
27
|
+
menu_group(entry)
|
28
|
+
elsif entry[:type] == :item
|
29
|
+
menu_item(entry)
|
30
|
+
else
|
31
|
+
fail "Unknown menu entry type: \"#{entry[:type]}\""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def menu_group(entry)
|
36
|
+
if entry[:level] == 0
|
37
|
+
menu_group_root(entry)
|
38
|
+
else
|
39
|
+
menu_group_sub(entry)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def menu_group_root(entry)
|
44
|
+
@view.content_tag(:li, class: 'dropdown') do
|
45
|
+
menu_group_root_button(entry) << menu_group_root_children(entry)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def menu_group_root_button(entry)
|
50
|
+
@view.content_tag(:a, :role => 'button', 'data-toggle' => 'dropdown',
|
51
|
+
:class => "btn #{@options[:button_class]}",
|
52
|
+
:'data-target' => '#') do
|
53
|
+
ActiveSupport::SafeBuffer.new(entry[:label]) << ' ' << @view.tag(:span, class: 'caret')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def menu_group_root_children(entry)
|
58
|
+
@view.content_tag(:ul, class: 'dropdown-menu multi-level', role: 'menu',
|
59
|
+
'aria-labelledby': 'dropdownMenu') do
|
60
|
+
menu_entries(entry[:children])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def menu_group_sub(entry)
|
65
|
+
@view.content_tag(:li, class: 'dropdown-submenu') do
|
66
|
+
@view.link_to(entry[:label], '#', tabindex: -1) <<
|
67
|
+
@view.content_tag(:ul, class: 'dropdown-menu') do
|
68
|
+
menu_entries(entry[:children])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def menu_item(entry)
|
74
|
+
@view.content_tag(:li) do
|
75
|
+
@view.link_to(entry[:label], entry[:path], menu_item_options(entry))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def menu_item_options(entry)
|
80
|
+
options = entry[:options].dup
|
81
|
+
if options.key?(:link_method)
|
82
|
+
options[:method] = options[:link_method]
|
83
|
+
options.delete(:link_method)
|
84
|
+
end
|
85
|
+
options[:class] = "#{@options[:button_class]}".strip if entry[:level] == 0
|
86
|
+
options
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Eac
|
2
|
+
module MenusHelper
|
3
|
+
class DataBuilder
|
4
|
+
def initialize(view)
|
5
|
+
@view = view
|
6
|
+
end
|
7
|
+
|
8
|
+
def build(entries)
|
9
|
+
build_entries(entries, 0)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def build_entries(entries, level)
|
15
|
+
fail 'Argument "entries" is not a hash' unless entries.is_a?(Hash)
|
16
|
+
r = entries.map { |k, v| build_entry(k, v, level) }.select { |e| e }
|
17
|
+
r.empty? ? nil : r
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_entry(key, value, level)
|
21
|
+
if value.is_a?(Hash)
|
22
|
+
build_group(key, value, level)
|
23
|
+
else
|
24
|
+
build_item(key, value, level)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_group(label, menu_entries, level)
|
29
|
+
e = build_entries(menu_entries, level + 1)
|
30
|
+
return nil unless e
|
31
|
+
{ type: :group, label: label, children: e, level: level }
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_item(label, value, level)
|
35
|
+
path, options = menu_item_options(value)
|
36
|
+
return nil unless can_access_path?(path, options[:link_method])
|
37
|
+
{ type: :item, label: label, path: path, options: options, level: level }
|
38
|
+
end
|
39
|
+
|
40
|
+
def menu_item_options(item_value)
|
41
|
+
if item_value.is_a?(Array)
|
42
|
+
[item_value[0], item_value[1] || {}]
|
43
|
+
else
|
44
|
+
[item_value, {}]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def can_access_path?(path, method)
|
49
|
+
if @view.respond_to?(:'can_by_path?')
|
50
|
+
@view.can_by_path?(path, method)
|
51
|
+
else
|
52
|
+
true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|