base-project 0.1.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 (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.travis.yml +5 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +196 -0
  7. data/README.md +43 -0
  8. data/Rakefile +6 -0
  9. data/base-project.gemspec +38 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/base/project.rb +7 -0
  13. data/lib/base/project/app/controllers/concerns/base_crud_controller.rb +104 -0
  14. data/lib/base/project/app/helpers/base_helper.rb +128 -0
  15. data/lib/base/project/app/helpers/button_helper.rb +101 -0
  16. data/lib/base/project/app/helpers/localizable_helper.rb +51 -0
  17. data/lib/base/project/app/helpers/panel_helper.rb +8 -0
  18. data/lib/base/project/app/models/concerns/addressable.rb +93 -0
  19. data/lib/base/project/app/models/concerns/base_user_concern.rb +39 -0
  20. data/lib/base/project/app/models/concerns/image_concern.rb +16 -0
  21. data/lib/base/project/app/models/concerns/localizable.rb +50 -0
  22. data/lib/base/project/app/models/concerns/scope_concern.rb +14 -0
  23. data/lib/base/project/app/validators/image_validator.rb +14 -0
  24. data/lib/base/project/app/views/_flash_messages.html.erb +12 -0
  25. data/lib/base/project/app/views/_menu.html.erb +16 -0
  26. data/lib/base/project/app/views/forms/_buttons.html.erb +4 -0
  27. data/lib/base/project/app/views/indexes/_buttons.html.erb +11 -0
  28. data/lib/base/project/app/views/indexes/_header.html.erb +10 -0
  29. data/lib/base/project/app/views/menu/_header_menu.html.erb +25 -0
  30. data/lib/base/project/app/views/menu/_sidebar.html.erb +44 -0
  31. data/lib/base/project/app/views/panels/_panel.html.erb +15 -0
  32. data/lib/base/project/app/views/versions/_model_dates.html.erb +12 -0
  33. data/lib/base/project/config/locales/error_pages.pt-BR.yml +9 -0
  34. data/lib/base/project/config/locales/pt-BR.yml +269 -0
  35. data/lib/base/project/config/locales/simple_form.pt-BR.yml +76 -0
  36. data/lib/base/project/lib/console_say.rb +17 -0
  37. data/lib/base/project/lib/string_sanitizer.rb +54 -0
  38. data/lib/base/project/version.rb +5 -0
  39. metadata +277 -0
@@ -0,0 +1,7 @@
1
+ require "base/project/version"
2
+
3
+ module Base
4
+ module Project
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,104 @@
1
+
2
+ module Base::Project::App::Controllers::Concerns::BaseCrudController
3
+ extend ActiveSupport::Concern
4
+
5
+ @serializer_class ||= nil
6
+
7
+ included do
8
+ before_action :check_default_permission
9
+ before_action :set_model, only: %i[show update destroy]
10
+
11
+ def index
12
+ @models = apply_scopes(model_source)
13
+
14
+ respond_with(@models) do |format|
15
+ format.html { flash.now[:info] = t('simple_form.results', count: @models.size) }
16
+ format.json { render json: @models, serializer_each: @serializer_class }
17
+ end
18
+ end
19
+
20
+ def show
21
+ respond_with(@model) do |format|
22
+ format.html {}
23
+ format.json { render json: @model, serializer: @serializer_class }
24
+ end
25
+ end
26
+
27
+ def new
28
+ @model = model_source.new
29
+
30
+ respond_with(@model) do |format|
31
+ format.html {}
32
+ format.json { render json: @model, serializer: @serializer_class }
33
+ end
34
+ end
35
+
36
+ def create
37
+ @model = model_source.new(model_params)
38
+ is_save = @model.save
39
+
40
+ respond_with(@model) do |format|
41
+ format.html do
42
+ if is_save
43
+ redirect_to created_path, notice: t('simple_form.added', klass: model_name)
44
+ else
45
+ flash.now[:error] = t('simple_form.error_notification.default_message')
46
+ render action: 'new'
47
+ end
48
+ end
49
+
50
+ format.json do
51
+ if is_save
52
+ head :created
53
+ else
54
+ render json: @model.errors, status: :unprocessable_entity
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def update
61
+ is_save = @model.update(update_model_params)
62
+
63
+ respond_with(@model) do |format|
64
+ format.html do
65
+ if is_save
66
+ flash.now[:success] = t('simple_form.updated', klass: model_name)
67
+ else
68
+ flash.now[:error] = t('simple_form.error_notification.default_message')
69
+ render action: 'show'
70
+ end
71
+ end
72
+
73
+ format.json do
74
+ if is_save
75
+ head :no_content
76
+ else
77
+ render json: @model.errors, status: :unprocessable_entity
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def destroy
84
+ @model.destroy
85
+
86
+ respond_to do |format|
87
+ format.html { redirect_to destroyed_path, notice: t('simple_form.removed', klass: model_name) }
88
+ format.json { head :no_content }
89
+ end
90
+ end
91
+
92
+ private
93
+
94
+ def set_model
95
+ @model = model_source.find(params[:id])
96
+ end
97
+ end
98
+
99
+ module ClassMethods
100
+ def serializer(klass)
101
+ @serializer_class = klass
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,128 @@
1
+
2
+ module Base::Project::App::Helpers::BaseHelper
3
+ def bootstrap_class_for(flash_type)
4
+ case flash_type.to_sym
5
+ when :success
6
+ 'alert-success' # Green
7
+ when :error
8
+ 'alert-danger' # Red
9
+ when :alert
10
+ 'alert-warning' # Yellow
11
+ when :notice
12
+ 'alert-success' # Blue
13
+ when :info
14
+ 'alert-info' # Blue
15
+ else
16
+ flash_type.to_s
17
+ end
18
+ end
19
+
20
+ def show_flash(type, message)
21
+ klass = bootstrap_class_for(type)
22
+
23
+ content_tag(:div, class: "alert alert-dismissible fade in #{klass} flash-message", role: 'alert') do
24
+ concat(button_tag(type: 'button', class: 'close', data: { dismiss: 'alert', label: 'Close' }) do
25
+ content_tag(:span, 'x', aria: { hidden: true })
26
+ end)
27
+
28
+ concat(message)
29
+ end
30
+ end
31
+
32
+ def show_flashes(flash)
33
+ flash.each do |type, message|
34
+ if message.is_a?(Array)
35
+ message.each do |text|
36
+ show_flash(type, text)
37
+ end
38
+
39
+ else
40
+ show_flash(type, message)
41
+ end
42
+ end
43
+ end
44
+
45
+ def page_title(text)
46
+ title = t('application_name')
47
+ text.blank? ? title : "#{title} - #{text}"
48
+ end
49
+
50
+ def nested_text_field(options = {})
51
+ text_field_tag(options[:field_name], options[:value], type: :text, class: "form-control #{options[:class]}")
52
+ end
53
+
54
+ def nested_select(options = {})
55
+ select_tag(options[:field_name], options[:value], class: "form-control tag-select #{options[:class]}")
56
+ end
57
+
58
+ def menu_item(path, icon, text, additional = {})
59
+ options = { method: :get, remote: false }.merge(additional)
60
+
61
+ content_tag(:li) do
62
+ link_to path, method: options[:method], remote: options[:remote] do
63
+ concat(content_tag(:i, '', class: "#{icon.split('-').first} #{icon}"))
64
+ concat(text)
65
+ end
66
+ end
67
+ end
68
+
69
+ def log_out_menu_item(path, icon, text)
70
+ menu_item(path, icon, text, method: :delete)
71
+ end
72
+
73
+ def icon_link(path, icon, text)
74
+ link_to(path) do
75
+ concat content_tag(:span, '', class: icon)
76
+ concat ' '
77
+ concat text
78
+ end
79
+ end
80
+
81
+ def brand_link
82
+ link_to(t('application_name'), authenticated_user_path, class: 'navbar-brand')
83
+ end
84
+
85
+ def link_image(image, options = {})
86
+ link_to(image.url(:big), target: '_blank', title: options[:title]) do
87
+ image_tag(image.url(:thumb), class: 'text-center img-circle', title: options[:title])
88
+ end
89
+ end
90
+
91
+ def edit_link(destination)
92
+ link_to(content_tag(:span, '', class: 'fa fa-pencil'), destination, class: 'pull-right')
93
+ end
94
+
95
+ def cancel_link(destination)
96
+ round_icon_link(path: destination, type: :button, icon: 'fa-close', button: 'btn-default', class: 'pull-right m-l-5', title: t('wizard.button.cancel'))
97
+ end
98
+
99
+ def submit_link
100
+ round_icon_button type: :submit, icon: 'fa-save', button: 'btn-success', class: 'pull-right', title: t('wizard.button.finish')
101
+ end
102
+
103
+ def round_icon_link(params)
104
+ params[:method] ||= :get
105
+ params[:remote] ||= false
106
+ data = {}
107
+
108
+ data[:confirm] = params[:confirm] if params[:confirm]
109
+
110
+ link_to(params[:path], method: params[:method], data: data, remote: params[:remote]) do
111
+ round_icon_button(params)
112
+ end
113
+ end
114
+
115
+ def text_icon_link(params)
116
+ params[:shape] = ''
117
+ params[:text] = params[:title]
118
+ params[:method] ||= :get
119
+ params[:remote] ||= false
120
+ data = {}
121
+
122
+ data[:confirm] = params[:confirm] if params[:confirm]
123
+
124
+ link_to(params[:path], method: params[:method], data: data, remote: params[:remote]) do
125
+ text_icon_button(params)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,101 @@
1
+
2
+ module Base::Project::App::Helpers::ButtonHelper
3
+ def command_button( path, row_id, message, additional = { } )
4
+ options = { remote: false, role: :get, target: '_self', klass: '',
5
+ text: "", confirm: false, data: { } }.merge( additional )
6
+
7
+ text = options[:text]
8
+ role = options[:role]
9
+ icon = options[:icon]
10
+ klass = options[:klass]
11
+ is_remote = options[:remote]
12
+ html_data = options[:data]
13
+
14
+ data_params = html_data.merge( { toggle: 'tooltip', placement: 'top', 'row-id': row_id, 'original-title': message } )
15
+ data_params[:confirm] = I18n.t('confirmation') if options[:confirm]
16
+
17
+ button = command_button_item( klass, icon, text, data_params )
18
+
19
+ if path.present?
20
+ link_to(path, remote: is_remote, data: data_params, target: options[:target], method: role) do
21
+ concat( button )
22
+ end
23
+
24
+ else
25
+ button
26
+ end
27
+ end
28
+
29
+ def command_button_item( klass, icon, text, data )
30
+ text = " #{text}" if text.present?
31
+
32
+ button_tag( role: 'button', type: 'button', class: "btn btn-primary #{ klass }", data: data ) do
33
+ content_tag( :span, text, class: icon )
34
+ end
35
+ end
36
+
37
+ def edit_button( path, row_id, message, additional = { } )
38
+ options = { role: :get, icon: 'zmdi zmdi-edit' }.merge( additional )
39
+ command_button( path, row_id, I18n.t('simple_form.edit', message: message), options )
40
+ end
41
+
42
+ def new_button( path, message, additional = { } )
43
+ options = { role: :get, icon: 'zmdi zmdi-plus' }.merge( additional )
44
+ command_button( path, 0, I18n.t('simple_form.new', message: message), options )
45
+ end
46
+
47
+ def delete_button( path, row_id, message, additional = { } )
48
+ options = { role: :delete, icon: 'zmdi zmdi-delete', klass: 'command-delete', confirm: true }.merge( additional )
49
+ command_button( path, row_id, I18n.t('simple_form.delete', message: message), options )
50
+ end
51
+
52
+ def show_button( path, row_id, message, additional = { } )
53
+ options = { role: :get, icon: 'zmdi zmdi-search' }.merge( additional )
54
+ command_button( path, row_id, I18n.t('simple_form.show', message: message), options )
55
+ end
56
+
57
+ def open_filter_button
58
+ button_tag( t('simple_form.filter'), id: 'enable-filter',
59
+ class: 'btn btn-regular pull-left' )
60
+ end
61
+
62
+ def do_filter_button
63
+ button_tag( t('simple_form.search'), id: 'enable-filter',
64
+ class: 'btn btn-primary pull-right' )
65
+ end
66
+
67
+ def cancel_button
68
+ round_icon_button(type: :button, icon: 'fa-close', button: 'btn-default', class: 'pull-right', title: 'Cancelar')
69
+ end
70
+
71
+ def round_icon_button(params)
72
+ params[:shape] = 'btn-circle'
73
+ params[:text] = ''
74
+
75
+ text_icon_button(params)
76
+ end
77
+
78
+ def text_icon_button(params)
79
+ params[:size] ||= 'btn-lg'
80
+
81
+ content = content_tag(:span) do
82
+ concat(content_tag(:span, '', class: "fa #{params[:icon]}"))
83
+ concat(" #{params[:text]}") if params[:text] && params[:text].length > 0
84
+ end
85
+
86
+ button_data = { type: params[:type],
87
+ class: "btn #{params[:button]} #{params[:shape]} #{params[:size]} #{params[:class]} text-center",
88
+ title: params[:title],
89
+ data: {
90
+ toggle: 'tooltip',
91
+ placement: 'bottom'
92
+ }
93
+ }
94
+
95
+
96
+ button_data[:id] = params[:id] if params[:id]
97
+ button_data[:data] = button_data[:data].merge(params[:data]) if params[:data]
98
+
99
+ button_tag(content, button_data)
100
+ end
101
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module Base::Project::App::Helpers::LocalizableHelper
3
+ def prepare_coordinates(location)
4
+ coordinate_array = location.coordinates
5
+ coordinate_array.present? ? escape_javascript(coordinate_array.to_json).to_s : ''
6
+ end
7
+
8
+ def add_localization(location)
9
+ add_localizations([location]) if location
10
+ end
11
+
12
+ def add_localizations(locations)
13
+ return '' if locations.blank?
14
+
15
+ js_locations = []
16
+ first_location = locations.first
17
+
18
+ been_img = image_url('map/been.png')
19
+ current_img = image_url('map/employee.png')
20
+
21
+ locations.each do |location|
22
+ coordinates = prepare_coordinates(location)
23
+ next if coordinates.blank?
24
+
25
+ if location != first_location
26
+ image = been_img
27
+ current = true
28
+ else
29
+ image = current_img
30
+ current = false
31
+ end
32
+
33
+ location_id = "loc-#{location.id}"
34
+
35
+ js_locations << "window.map.addCoordinate({
36
+ coordinates: #{coordinates},
37
+ icon: '#{image}',
38
+ locationId: '#{location_id}',
39
+ visited: #{current},
40
+ period: true,
41
+ properties: { title: '#{location.name}', icon: '#{image}' }
42
+ });"
43
+ end
44
+
45
+ js_locations << "window.map.getMap().setCenter(#{prepare_coordinates(first_location)});"
46
+ js_locations << 'window.map.getMap().setZoom(15);'
47
+
48
+ result = js_locations.join("\n")
49
+ result.html_safe
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Base::Project::App::Helpers::PanelHelper
3
+ def do_panel(params, &block)
4
+ params[:block] = block
5
+ params[:color] ||= 'panel-primary'
6
+ render 'shared/panels/panel', params
7
+ end
8
+ end
@@ -0,0 +1,93 @@
1
+ module Base::Project::App::Models::Concerns::Addressable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_validation :coordinates_precision
6
+ normalize_attributes :zipcode, with: :remove_punctuation
7
+
8
+ validates :default, inclusion: { in: [true, false] }
9
+ validates :name, presence: true, length: { maximum: 100 }
10
+
11
+ validates :country, presence: true, length: { maximum: 2, minimum: 2 }
12
+ validates :state, presence: true, length: { maximum: 100 }
13
+ validates :city, presence: true, length: { maximum: 100 }
14
+ validates :street, presence: true, length: { maximum: 100 }
15
+ validates :number, presence: true, numericality: { greater_than_or_equal_to: 0 }
16
+ validates :district, presence: true, length: { maximum: 100 }
17
+ validates :zipcode, presence: true, length: { maximum: 8, minimmum: 8 }, format: { with: /\A\d*\z/i }
18
+
19
+ validates :apartment, presence: false, numericality: { greater_than: 0 }, allow_blank: true
20
+ validates :block, presence: false, length: { maximum: 100 }, allow_blank: true
21
+
22
+ validates :latitude, presence: false, allow_blank: true
23
+ validates :longitude, presence: false, allow_blank: true
24
+ validate :unique_default_address
25
+
26
+ scope :by_name, ->(name) { where("#{table_name}.name ILIKE ?", "%#{name}%") }
27
+ scope :by_country, ->(country) { where("#{table_name}.country ILIKE ?", "%#{country}%") }
28
+ scope :by_city, ->(city) { where("#{table_name}.city ILIKE ?", "%#{city}%") }
29
+ scope :by_street, ->(street) { where("#{table_name}.street ILIKE ?", "%#{street}%") }
30
+ scope :by_number, ->(number) { where("#{table_name}.number = ?", number.to_i.to_s) }
31
+ scope :by_district, ->(district) { where("#{table_name}.district ILIKE ?", "%#{district}%") }
32
+ scope :by_zipcode, ->(zipcode) { where("#{table_name}.zipcode ILIKE ?", "%#{Base::Project::Lib::StringSanitizer.remove_punctuation(zipcode)}%") }
33
+
34
+ scope :regular_order, -> { order("#{table_name}.default DESC, #{table_name}.name") }
35
+ scope :the_defaults, -> { where("#{table_name}.default = ?", true) }
36
+ end
37
+
38
+ def coordinates_precision
39
+ self.latitude = latitude.round(11) if latitude.present?
40
+ self.longitude = longitude.round(11) if longitude.present?
41
+ end
42
+
43
+ def multiple_default_error
44
+ errors.add(:default, I18n.t('activerecord.errors.messages.default_address_already_exists'))
45
+ end
46
+
47
+ def formatted_simple
48
+ text = "#{street} #{number}"
49
+
50
+ text = "#{text}, ap.#{apartment}" if apartment
51
+ text = "#{text}, bl.#{block}" if block
52
+
53
+ "#{text}, #{district}, #{city}"
54
+ end
55
+
56
+ def formatted_zipcode
57
+ StringSanitizer.mask_cep(zipcode)
58
+ end
59
+
60
+ def formatted_country
61
+ country_model = ISO3166::Country[country]
62
+ country_model.translations[country] || country_model.name
63
+ end
64
+
65
+ def formatted_complete
66
+ "#{formatted_simple}, #{formatted_zipcode} #{formatted_country}"
67
+ end
68
+
69
+ def coordinate_pair
70
+ [latitude, longitude]
71
+ end
72
+
73
+ def coordinates_dms
74
+ latitude_dms = coordinate_to_dms(latitude, %w[N S])
75
+ longitude_dms = coordinate_to_dms(longitude, %w[E W])
76
+
77
+ [latitude_dms, longitude_dms]
78
+ end
79
+
80
+ private
81
+
82
+ def coordinate_to_dms(coordinate_value, directions)
83
+ value = coordinate_value.abs
84
+
85
+ degrees = value.floor
86
+ direction = (latitude > 0 ? directions.first : directions.last)
87
+
88
+ minutes = (((value - degrees) * 60)).round(3).floor
89
+ seconds = (((value - degrees) * 3600) - (minutes * 60)).round(3).floor
90
+
91
+ "#{degrees}° #{minutes}′ #{seconds}\"#{direction}"
92
+ end
93
+ end