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