base-project 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,39 @@
1
+
2
+ module Base::Project::App::Models::Concerns::BaseUserConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # Include default devise modules. Others available are:
7
+ # :confirmable, :lockable, :timeoutable and :omniauthable
8
+ devise :database_authenticatable, :registerable,
9
+ :recoverable, :rememberable, :trackable, :validatable
10
+
11
+ before_save :check_password_changed
12
+
13
+ normalize_attributes :cpf, with: :remove_punctuation, if: ->(_attr) { cpf.present? }
14
+ validates_cpf_format_of :cpf, if: ->(_attr) { cpf.present? }
15
+
16
+ validates :cpf, allow_blank: true, uniqueness: true
17
+ validates :name, presence: true, uniqueness: false
18
+ validates :email, presence: true, uniqueness: true, format: { with: Devise.email_regexp }
19
+ validates :updated_password, inclusion: { in: [true, false] }
20
+
21
+ scope :by_cpf, ->(cpf) { where("#{table_name}.cpf ILIKE ?", "%#{Base::Project::Lib::StringSanitizer.remove_punctuation(cpf)}%") }
22
+ scope :by_email, ->(email) { where("#{table_name}.email ILIKE ?", "%#{email}%") }
23
+ scope :by_name, ->(name) { where("unaccent(#{table_name}.name) ILIKE ?", "%#{name}%") }
24
+
25
+ def password_required?
26
+ new_record? ? false : super
27
+ end
28
+
29
+ private
30
+
31
+ def check_password_changed
32
+ if changed.include?('encrypted_password')
33
+ self.updated_password = attributes['encrypted_password'] != encrypted_password_was
34
+ end
35
+
36
+ nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Base::Project::App::Models::Concerns::ImageConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_attached_file :image, default_url: ':style/missing.png',
7
+ path: ':class/:id/:style.:extension',
8
+ styles: { regular: ['250x250#', :png],
9
+ big: ['500x500#', :png],
10
+ medium: ['300x300#', :png],
11
+ small: ['150x150#', :png],
12
+ thumb: ['50x50#', :png] }
13
+
14
+ validates_attachment :image, content_type: { content_type: /\Aimage\/.*\Z/ }, size: { less_than: 2.megabytes }
15
+ end
16
+ end
@@ -0,0 +1,50 @@
1
+ module Base::Project::App::Models::Concerns::Localizable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ scope :by_coord, ->(latitude, longitude) { near(latitude, longitude, 1) }
6
+
7
+ scope :near, ->(lat, lng, radius) {
8
+ d = ->(b) { destination_point(lat, lng, b, radius) }
9
+
10
+ where(['latitude BETWEEN ? AND ? AND longitude BETWEEN ? AND ?', d[180][:lat],
11
+ d[0][:lat],
12
+ d[270][:lng],
13
+ d[90][:lng]])
14
+ .where(['COALESCE(DISTANCE(?, ?, latitude, longitude), 0) < ?', lat, lng, radius])
15
+ .order(:id)
16
+ }
17
+ end
18
+
19
+ def coordinates
20
+ [longitude, latitude] if latitude && longitude
21
+ end
22
+
23
+ module ClassMethods
24
+ # Return destination point given distance and bearing from start point
25
+ def destination_point(lat, lng, initial_bearing, distance)
26
+ d2r = ->(x) { x * Math::PI / 180 }
27
+ r2d = ->(x) { x * 180 / Math::PI }
28
+
29
+ lat ||= 0
30
+ lng ||= 0
31
+
32
+ angular_distance = distance / 6371.0
33
+
34
+ lat1 = d2r.call(lat)
35
+ lng1 = d2r.call(lng)
36
+ bearing = d2r.call(initial_bearing)
37
+
38
+ lat2 = Math.asin(Math.sin(lat1) *
39
+ Math.cos(angular_distance) +
40
+ Math.cos(lat1) *
41
+ Math.sin(angular_distance) *
42
+ Math.cos(bearing))
43
+
44
+ lng2 = lng1 + Math.atan2(Math.sin(bearing) * Math.sin(angular_distance) * Math.cos(lat1),
45
+ Math.cos(angular_distance) - Math.sin(lat1) * Math.sin(lat2))
46
+
47
+ { lat: r2d.call(lat2).round(7), lng: r2d.call(lng2).round(7) }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+
2
+ module Base::Project::App::Models::Concerns::ScopeConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ scope :created_since, ->(time) { where("#{table_name}.created_at >= ?", Date.parse(time).beginning_of_day) }
7
+ scope :created_until, ->(time) { where("#{table_name}.created_at <= ?", Date.parse(time).end_of_day) }
8
+ scope :created_between, ->(from, to) { created_since(from).created_until(to) }
9
+
10
+ scope :updated_since, ->(time) { where("#{table_name}.updated_at >= ?", Date.parse(time).beginning_of_day) }
11
+ scope :updated_until, ->(time) { where("#{table_name}.updated_at <= ?", Date.parse(time).end_of_day) }
12
+ scope :updated_between, ->(from, to) { updated_since(from).updated_until(to) }
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+
2
+
3
+ class Base::Project::App::Validators::ImageValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ return if value.queued_for_write.blank? || value.queued_for_write[:original].blank?
6
+
7
+ dimensions = Paperclip::Geometry.from_file(value.queued_for_write[:original].path)
8
+ width = options[:width]
9
+ height = options[:height]
10
+
11
+ record.errors.add(attribute, I18n.t('activerecord.errors.image.width', value: width)) if dimensions.width > width
12
+ record.errors.add(attribute, I18n.t('activerecord.errors.image.height', value: height)) if dimensions.height > height
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ <div class="col-xs-12 m-0 p-0" id="alert-location">
2
+ <% flash.each do |type, message| %>
3
+ <% if message.kind_of?(Array) %>
4
+ <% message.each do |text| %>
5
+ <%= show_flash( type, text ) %>
6
+ <% end %>
7
+
8
+ <% else %>
9
+ <%= show_flash( type, message ) %>
10
+ <% end %>
11
+ <% end %>
12
+ </div>
@@ -0,0 +1,16 @@
1
+ <nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
2
+ <div class="navbar-header">
3
+ <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
4
+ <span class="sr-only"><%= t('toggle_menu') %></span>
5
+ <span class="icon-bar"></span>
6
+ <span class="icon-bar"></span>
7
+ <span class="icon-bar"></span>
8
+ </button>
9
+
10
+ <%= image_tag 'android-icon-48x48', class: 'pull-left' %>
11
+ <%= brand_link %>
12
+ </div>
13
+
14
+ <%= render 'shared/menu/header_menu' %>
15
+ <%= render 'shared/menu/sidebar' %>
16
+ </nav>
@@ -0,0 +1,4 @@
1
+ <div class="col-xs-12 p-0 m-0 m-t-10">
2
+ <%= cancel_link(cancel_link) %>
3
+ <%= submit_link %>
4
+ </div>
@@ -0,0 +1,11 @@
1
+ <h2>
2
+ <div class="col-xs-12">
3
+ <%= round_icon_link button: 'btn-default', class: 'pull-left', icon: 'fa-arrow-left', path: back_link, title:t('simple_form.back') %>
4
+
5
+ <% if defined?(new_link) %>
6
+ <%= round_icon_link button: 'btn-default', class: 'pull-right m-l-5', icon: 'fa-plus', path: new_link, title: new_title %>
7
+ <% end %>
8
+
9
+ <%= round_icon_button type: :button, button: 'btn-info', icon: 'fa-filter', class: 'display-filters pull-right', title: t('filters.open_button') %>
10
+ </div>
11
+ </h2>
@@ -0,0 +1,10 @@
1
+ <h1 class="page-header">
2
+ <%= name %>
3
+
4
+ <% if defined?(parent) && parent %>
5
+ |
6
+ <span class="additional-page-header">
7
+ <%= link_to parent.name, url_for(parent), class: 'no-link' %>
8
+ </span>
9
+ <% end %>
10
+ </h1>
@@ -0,0 +1,25 @@
1
+ <ul class="nav navbar-top-links navbar-right">
2
+
3
+ <% if current_user %>
4
+ <li class="dropdown">
5
+ <a class="dropdown-toggle" data-toggle="dropdown" href="#">
6
+ <i class="fa fa-user fa-fw"></i>
7
+ <i class="fa fa-caret-down"></i>
8
+ </a>
9
+
10
+ <ul class="dropdown-menu dropdown-user">
11
+ <li>
12
+ <%= edit_profile_button %>
13
+ </li>
14
+
15
+ <% if current_user.updated_password %>
16
+ <li class="divider"></li>
17
+ <li>
18
+ <%= logout_button %>
19
+ </li>
20
+ <% end %>
21
+ </ul>
22
+ </li>
23
+ <% end %>
24
+
25
+ </ul>
@@ -0,0 +1,44 @@
1
+ <div class="navbar-default sidebar" role="navigation">
2
+ <div class="sidebar-nav navbar-collapse">
3
+
4
+ <% if current_user && current_user.updated_password %>
5
+ <ul class="nav in" id="side-menu">
6
+ <li class="sidebar-search">
7
+ <div class="input-group custom-search-form">
8
+ <input class="form-control" placeholder="<%= t('search_menu') %>" type="text">
9
+
10
+ <span class="input-group-btn">
11
+ <button class="btn btn-default" type="button">
12
+ <i class="fa fa-search"></i>
13
+ </button>
14
+ </span>
15
+ </div>
16
+ </li>
17
+
18
+ <% with_permission('dashboard|index') do %>
19
+ <li>
20
+ <%= icon_link(authenticated_user_path, 'fa fa-dashboard fa-fw', t('activerecord.models.dashboards.other')) %>
21
+ </li>
22
+ <% end %>
23
+
24
+ <% with_permission('user_type|index') do %>
25
+ <li>
26
+ <%= icon_link(user_types_path, 'fa fa-users fa-fw', t('activerecord.models.user_types.other')) %>
27
+ </li>
28
+ <% end %>
29
+
30
+ <% with_permission('employer|index') do %>
31
+ <li>
32
+ <%= icon_link(employers_path, 'fa fa-building-o fa-fw', t('activerecord.models.employers.other')) %>
33
+ </li>
34
+ <% end %>
35
+
36
+ <% with_permission('user|index') do %>
37
+ <li>
38
+ <%= icon_link(users_path, 'fa fa-users fa-fw', t('activerecord.models.users.other')) %>
39
+ </li>
40
+ <% end %>
41
+ </ul>
42
+ <% end %>
43
+ </div>
44
+ </div>
@@ -0,0 +1,15 @@
1
+ <div class="panel <%= color %>">
2
+ <div class="panel-heading">
3
+ <h4 class="panel-title">
4
+ <a data-toggle="collapse" href="#<%= panel_id %>" aria-expanded="true" class="">
5
+ <%= title %>
6
+ </a>
7
+ </h4>
8
+ </div>
9
+
10
+ <div id="<%= panel_id %>" class="panel-collapse collapse in" aria-expanded="true" style="">
11
+ <div class="panel-body">
12
+ <%= capture(&block) %>
13
+ </div>
14
+ </div>
15
+ </div>
@@ -0,0 +1,12 @@
1
+ <% if form.object.persisted? %>
2
+ <%= form.input :created_at, as: :string, wrapper_html: { class: 'col-xs-4' }, input_html: {disabled: true, value: localize(form.object.created_at, format: :small)} %>
3
+ <%= form.input :updated_at, as: :string, wrapper_html: { class: 'col-xs-4' }, input_html: {disabled: true, value: localize(form.object.updated_at, format: :small)} %>
4
+
5
+ <div class="form-group string optional client_whodunnit col-xs-4">
6
+ <label class="control-label string optional" for="client_whodunnit">
7
+ <%= t('activerecord.attributes.version.whodunnit') %>
8
+ </label>
9
+
10
+ <input class="form-control string optional" value="<%= form.object.versions.last.whodunnit%>" name="client[whodunnit]" id="client_whodunnit" type="text" disabled>
11
+ </div>
12
+ <% end %>
@@ -0,0 +1,9 @@
1
+ pt-BR:
2
+ error_pages:
3
+ code: Status %{code}
4
+ 401: Você não possui permissão para essa ação
5
+ 404: Infelizmente, não foi possível encontrar o recurso
6
+ 422: Não foi possível realizar a ação desejada
7
+ 500: Aconteceu um erro inesperado!
8
+ 503: O sistema está indisponível no momento
9
+ additional: Por favor, contate o Administrador do Sistema
@@ -0,0 +1,269 @@
1
+
2
+ pt-BR:
3
+ application_name: Envlab - Atmosférico
4
+ toggle_menu: Abre menu
5
+ search_menu: Pesquisar...
6
+ visualize_button: Visualizar
7
+
8
+ date:
9
+ abbr_day_names:
10
+ - Dom
11
+ - Seg
12
+ - Ter
13
+ - Qua
14
+ - Qui
15
+ - Sex
16
+ - Sáb
17
+
18
+ abbr_month_names:
19
+ -
20
+ - Jan
21
+ - Fev
22
+ - Mar
23
+ - Abr
24
+ - Mai
25
+ - Jun
26
+ - Jul
27
+ - Ago
28
+ - Set
29
+ - Out
30
+ - Nov
31
+ - Dez
32
+
33
+ day_names:
34
+ - Domingo
35
+ - Segunda-feira
36
+ - Terça-feira
37
+ - Quarta-feira
38
+ - Quinta-feira
39
+ - Sexta-feira
40
+ - Sábado
41
+
42
+ formats:
43
+ default: "%d/%m/%Y"
44
+ long: "%d de %B de %Y"
45
+ short: "%d de %B"
46
+ short_month: "%B %Y"
47
+
48
+ month_names:
49
+ -
50
+ - Janeiro
51
+ - Fevereiro
52
+ - Março
53
+ - Abril
54
+ - Maio
55
+ - Junho
56
+ - Julho
57
+ - Agosto
58
+ - Setembro
59
+ - Outubro
60
+ - Novembro
61
+ - Dezembro
62
+
63
+ order:
64
+ - :day
65
+ - :month
66
+ - :year
67
+
68
+ datetime:
69
+ distance_in_words:
70
+ about_x_hours:
71
+ one: aproximadamente 1 hora
72
+ other: aproximadamente %{count} horas
73
+
74
+ about_x_months:
75
+ one: aproximadamente 1 mês
76
+ other: aproximadamente %{count} meses
77
+
78
+ about_x_years:
79
+ one: aproximadamente 1 ano
80
+ other: aproximadamente %{count} anos
81
+
82
+ almost_x_years:
83
+ one: quase 1 ano
84
+ other: quase %{count} anos
85
+
86
+ half_a_minute: meio minuto
87
+
88
+ less_than_x_minutes:
89
+ one: menos de um minuto
90
+ other: menos de %{count} minutos
91
+
92
+ less_than_x_seconds:
93
+ one: menos de 1 segundo
94
+ other: menos de %{count} segundos
95
+
96
+ over_x_years:
97
+ one: mais de 1 ano
98
+ other: mais de %{count} anos
99
+
100
+ x_days:
101
+ one: 1 dia
102
+ other: "%{count} dias"
103
+
104
+ x_minutes:
105
+ one: 1 minuto
106
+ other: "%{count} minutos"
107
+
108
+ x_months:
109
+ one: 1 mês
110
+ other: "%{count} meses"
111
+
112
+ x_seconds:
113
+ one: 1 segundo
114
+ other: "%{count} segundos"
115
+
116
+ prompts:
117
+ day: Dia
118
+ hour: Hora
119
+ minute: Minuto
120
+ month: Mês
121
+ second: Segundo
122
+ year: Ano
123
+
124
+ errors:
125
+ connection_refused: Conexão recusada
126
+ format: "%{attribute} %{message}"
127
+
128
+ messages:
129
+ accepted: deve ser aceito
130
+ blank: não pode ficar em branco
131
+ present: deve ficar em branco
132
+ confirmation: não é igual a %{attribute}
133
+ empty: não pode ficar vazio
134
+ equal_to: deve ser igual a %{count}
135
+ even: deve ser par
136
+ exclusion: não está disponível
137
+ greater_than: deve ser maior que %{count}
138
+ greater_than_or_equal_to: deve ser maior ou igual a %{count}
139
+ in_between: "deve ter entre %{min} e %{max}"
140
+ inclusion: não está incluído na lista
141
+ invalid: não é válido
142
+ less_than: deve ser menor que %{count}
143
+ less_than_or_equal_to: deve ser menor ou igual a %{count}
144
+ model_invalid: 'A validação falhou: %{errors}'
145
+ not_a_number: não é um número
146
+ not_an_integer: não é um número inteiro
147
+ odd: deve ser ímpar
148
+ required: deve existir
149
+ spoofed_media_type: "tem uma extensão que não corresponde ao seu conteúdo"
150
+ taken: já está em uso
151
+
152
+ too_long:
153
+ one: 'é muito longo (máximo: 1 caracter)'
154
+ other: 'é muito longo (máximo: %{count} caracteres)'
155
+
156
+ too_short:
157
+ one: 'é muito curto (mínimo: 1 caracter)'
158
+ other: 'é muito curto (mínimo: %{count} caracteres)'
159
+
160
+ wrong_length:
161
+ one: não possui o tamanho esperado (1 caracter)
162
+ other: não possui o tamanho esperado (%{count} caracteres)
163
+
164
+ other_than: deve ser diferente de %{count}
165
+
166
+ template:
167
+ body: 'Por favor, verifique o(s) seguinte(s) campo(s):'
168
+
169
+ header:
170
+ one: 'Não foi possível gravar %{model}: 1 erro'
171
+ other: 'Não foi possível gravar %{model}: %{count} erros'
172
+
173
+ helpers:
174
+ select:
175
+ prompt: Por favor selecione
176
+
177
+ submit:
178
+ create: Criar %{model}
179
+ submit: Salvar %{model}
180
+ update: Atualizar %{model}
181
+
182
+ number:
183
+ currency:
184
+ format:
185
+ delimiter: "."
186
+ format: "%u %n"
187
+ precision: 2
188
+ separator: ","
189
+ significant: false
190
+ strip_insignificant_zeros: false
191
+ unit: R$
192
+
193
+ format:
194
+ delimiter: "."
195
+ precision: 3
196
+ separator: ","
197
+ significant: false
198
+ strip_insignificant_zeros: false
199
+
200
+ human:
201
+ decimal_units:
202
+ format: "%n %u"
203
+ units:
204
+ billion:
205
+ one: bilhão
206
+ other: bilhões
207
+
208
+ million:
209
+ one: milhão
210
+ other: milhões
211
+
212
+ quadrillion:
213
+ one: quatrilhão
214
+ other: quatrilhões
215
+
216
+ thousand: mil
217
+
218
+ trillion:
219
+ one: trilhão
220
+ other: trilhões
221
+
222
+ unit: ''
223
+
224
+ format:
225
+ delimiter: "."
226
+ precision: 2
227
+ significant: true
228
+ strip_insignificant_zeros: true
229
+
230
+ storage_units:
231
+ format: "%n %u"
232
+
233
+ units:
234
+ byte:
235
+ one: Byte
236
+ other: Bytes
237
+
238
+ gb: GB
239
+ kb: KB
240
+ mb: MB
241
+ tb: TB
242
+
243
+ percentage:
244
+ format:
245
+ delimiter: "."
246
+ format: "%n%"
247
+
248
+ precision:
249
+ format:
250
+ delimiter: "."
251
+
252
+ support:
253
+ array:
254
+ last_word_connector: " e "
255
+ two_words_connector: " e "
256
+ words_connector: ", "
257
+
258
+ time:
259
+ am: ''
260
+
261
+ formats:
262
+ default: "%a, %d de %B de %Y, %H:%M:%S %z"
263
+ long: "%d de %B de %Y, %H:%M"
264
+ short: "%d de %B, %H:%M"
265
+ small: "%d/%m/%y, %H:%M"
266
+
267
+ pm: ''
268
+
269
+ confirmation: 'Você tem certeza?'