This commit is contained in:
f 2019-03-26 12:32:20 -03:00
parent 617619a493
commit fa90f7c21f
No known key found for this signature in database
GPG key ID: 2AE5A13E321F953D
76 changed files with 604 additions and 288 deletions

178
.rubocop_todo.yml Normal file
View file

@ -0,0 +1,178 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-03-26 12:32:08 -0300 using RuboCop version 0.66.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
# Offense count: 1
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleAlignWith, AutoCorrect, Severity.
# SupportedStylesAlignWith: keyword, variable, start_of_line
Layout/EndAlignment:
Exclude:
- 'app/models/post/image_uploader.rb'
# Offense count: 1
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'app/controllers/invitadxs_controller.rb'
# Offense count: 4
Lint/DuplicateMethods:
Exclude:
- 'app/models/post.rb'
- 'app/models/site.rb'
# Offense count: 1
Lint/HandleExceptions:
Exclude:
- 'app/controllers/posts_controller.rb'
# Offense count: 1
Lint/NonLocalExitFromIterator:
Exclude:
- 'app/models/invitadx.rb'
# Offense count: 1
Lint/ShadowingOuterLocalVariable:
Exclude:
- 'app/models/post.rb'
# Offense count: 3
Lint/UnderscorePrefixedVariableName:
Exclude:
- 'app/models/post.rb'
- 'app/models/post/template_field.rb'
- 'app/models/site.rb'
# Offense count: 2
Lint/UselessAssignment:
Exclude:
- 'app/models/invitadx.rb'
- 'app/models/site.rb'
# Offense count: 19
Metrics/AbcSize:
Max: 58
# Offense count: 3
# Configuration parameters: CountComments, ExcludedMethods.
# ExcludedMethods: refine
Metrics/BlockLength:
Max: 31
# Offense count: 3
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 379
# Offense count: 6
Metrics/CyclomaticComplexity:
Max: 18
# Offense count: 15
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/MethodLength:
Max: 34
# Offense count: 5
Metrics/PerceivedComplexity:
Max: 20
# Offense count: 1
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
Naming/MemoizedInstanceVariableName:
Exclude:
- 'app/models/post/image_uploader.rb'
# Offense count: 1
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
# NamePrefix: is_, has_, have_
# NamePrefixBlacklist: is_, has_, have_
# NameWhitelist: is_a?
# MethodDefinitionMacros: define_method, define_singleton_method
Naming/PredicateName:
Exclude:
- 'spec/**/*'
- 'app/models/post.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect.
Performance/TimesMap:
Exclude:
- 'app/models/site.rb'
- 'test/models/site_test.rb'
# Offense count: 102
# Configuration parameters: AllowedChars.
Style/AsciiComments:
Exclude:
- 'app/controllers/application_controller.rb'
- 'app/controllers/i18n_controller.rb'
- 'app/controllers/posts_controller.rb'
- 'app/controllers/sites_controller.rb'
- 'app/models/invitadx.rb'
- 'app/models/jekyll_i18n.rb'
- 'app/models/post.rb'
- 'app/models/post/image_uploader.rb'
- 'app/models/post/template_field.rb'
- 'app/models/site.rb'
- 'app/models/usuaria.rb'
- 'app/policies/post_policy.rb'
- 'config/routes.rb'
- 'lib/warden/imap.rb'
# Offense count: 4
# Cop supports --auto-correct.
# Configuration parameters: AutoCorrect, EnforcedStyle.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'app/models/post/image_uploader.rb'
- 'config/initializers/commonmarker.rb'
- 'config/initializers/warden.rb'
- 'test/test_helper.rb'
# Offense count: 25
Style/Documentation:
Enabled: false
# Offense count: 5
# Configuration parameters: MinBodyLength.
Style/GuardClause:
Exclude:
- 'app/controllers/posts_controller.rb'
- 'app/models/invitadx.rb'
- 'app/models/post.rb'
- 'lib/warden/email_and_password.rb'
# Offense count: 7
# Cop supports --auto-correct.
Style/IfUnlessModifier:
Exclude:
- 'app/models/invitadx.rb'
- 'app/models/jekyll_i18n.rb'
- 'app/models/post.rb'
- 'bin/bundle'
# Offense count: 2
Style/MixinUsage:
Exclude:
- 'bin/setup'
- 'bin/update'
# Offense count: 1
Style/OptionalArguments:
Exclude:
- 'app/models/site.rb'
# Offense count: 66
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 190

View file

@ -1,4 +1,5 @@
# frozen_string_literal: true
# Load DSL and Setup Up Stages
require 'capistrano/setup'

29
Gemfile
View file

@ -1,7 +1,9 @@
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
"https://github.com/#{repo_name}.git"
end
@ -30,21 +32,21 @@ gem 'bcrypt', '~> 3.1.7'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
gem 'bootstrap', '~> 4.0.0'
gem 'carrierwave'
gem 'carrierwave-bombshelter'
gem 'carrierwave-i18n'
gem 'commonmarker'
gem 'email_address'
gem 'rails_warden'
gem 'exception_notification'
gem 'font-awesome-rails'
gem 'haml-rails'
gem 'bootstrap', '~> 4.0.0'
gem 'jekyll'
gem 'jquery-rails'
gem 'font-awesome-rails'
gem 'exception_notification'
gem 'whenever', require: false
gem 'carrierwave'
gem 'carrierwave-i18n'
gem 'mini_magick'
gem 'carrierwave-bombshelter'
gem 'pundit'
gem 'rails_warden'
gem 'whenever', require: false
group :development, :test do
gem 'pry'
@ -55,17 +57,18 @@ end
group :development do
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'web-console', '>= 3.3.0'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem 'bcrypt_pbkdf'
gem 'capistrano'
gem 'capistrano-bundler'
gem 'capistrano-passenger'
gem 'capistrano-rails'
gem 'capistrano-rbenv'
gem 'rbnacl', '< 5.0'
gem 'letter_opener'
gem 'rbnacl', '< 5.0'
gem 'rubocop'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end

View file

@ -43,6 +43,7 @@ GEM
airbrussh (1.3.1)
sshkit (>= 1.6.1, != 1.7.0)
arel (8.0.0)
ast (2.4.0)
autoprefixer-rails (9.5.0)
execjs
bcrypt (3.1.12)
@ -134,6 +135,7 @@ GEM
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jaro_winkler (1.5.2)
jbuilder (2.8.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
@ -190,12 +192,16 @@ GEM
nio4r (2.3.1)
nokogiri (1.10.2)
mini_portile2 (~> 2.4.0)
parallel (1.15.0)
parser (2.6.2.0)
ast (~> 2.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
popper_js (1.14.5)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
psych (3.1.0)
public_suffix (3.0.3)
puma (3.12.1)
pundit (2.0.1)
@ -228,6 +234,7 @@ GEM
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (3.0.0)
rake (12.3.2)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
@ -235,8 +242,17 @@ GEM
rbnacl (4.0.2)
ffi
rouge (3.3.0)
rubocop (0.66.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.5, != 2.5.1.1)
psych (>= 3.1.0)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.6)
ruby-enum (0.7.2)
i18n
ruby-progressbar (1.10.0)
ruby_dep (1.5.0)
ruby_parser (3.13.0)
sexp_processor (~> 4.9)
@ -289,6 +305,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.5.0)
warden (1.2.8)
rack (>= 2.0.6)
web-console (3.7.0)
@ -338,6 +355,7 @@ DEPENDENCIES
rails (~> 5.1.4)
rails_warden
rbnacl (< 5.0)
rubocop
sass-rails (~> 5.0)
selenium-webdriver
spring

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Forma de ingreso a Sutty
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
@ -9,8 +11,7 @@ class ApplicationController < ActionController::Base
redirect_to sites_path
end
def markdown
end
def markdown; end
private

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class I18nController < ApplicationController
include Pundit
before_action :authenticate!
@ -15,7 +17,7 @@ class I18nController < ApplicationController
@lang_from = params.fetch(:from, I18n.locale.to_s)
@lang_to = params.fetch(:to, @lang_from)
@options = I18n.available_locales.map do |lang|
[ t("i18n.#{lang.to_s}"), lang.to_s ]
[t("i18n.#{lang}"), lang.to_s]
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class InvitadxsController < ApplicationController
include Pundit
@ -53,6 +55,6 @@ class InvitadxsController < ApplicationController
def invitadx_params
params.require(:invitadx).permit(:email, :password,
:password_confirmation, :acepta_politicas_de_privacidad)
:password_confirmation, :acepta_politicas_de_privacidad)
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class LoginController < ApplicationController
protect_from_forgery with: :exception
@ -15,7 +17,7 @@ class LoginController < ApplicationController
def create
authenticate
session[:lang] = params[:lang]
referer = request.referer unless /\/login/ =~ request.referer
referer = request.referer unless %r{/login} =~ request.referer
referer = params[:referer] if params[:referer].present?
if authenticated?

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PostsController < ApplicationController
include Pundit
before_action :authenticate!
@ -32,9 +34,9 @@ class PostsController < ApplicationController
@lang = find_lang(@site)
@template = find_template(@site)
@post = Post.new(site: @site,
front_matter: { date: Time.now },
lang: @lang,
template: @template)
front_matter: { date: Time.now },
lang: @lang,
template: @template)
end
def create
@ -47,17 +49,17 @@ class PostsController < ApplicationController
# El post se guarda como incompleto si se envió con "guardar para
# después"
@post.update_attributes({incomplete: params[:commit_incomplete].present?})
@post.update_attributes(incomplete: params[:commit_incomplete].present?)
# Las usuarias pueden especificar una autora, de la contrario por
# defecto es la usuaria actual
if current_user.is_a? Usuaria
@post.update_attributes({author: params[:post][:author]})
@post.update_attributes(author: params[:post][:author])
else
# Todo lo que crean lxs invitadxs es borrador
@post.update_attributes({draft: true})
@post.update_attributes(draft: true)
end
@post.update_attributes({author: current_user.username}) unless @post.author
@post.update_attributes(author: current_user.username) unless @post.author
if @post.save
flash[:success] = @site.config.dig('thanks')
@ -87,11 +89,11 @@ class PostsController < ApplicationController
# Solo las usuarias pueden modificar la autoría
if current_user.is_a? Usuaria
@post.update_attributes({author: params[:post][:author]}) if params[:post][:author].present?
@post.update_attributes({draft: false})
@post.update_attributes(author: params[:post][:author]) if params[:post][:author].present?
@post.update_attributes(draft: false)
else
# Todo lo que crean lxs invitadxs es borrador
@post.update_attributes({draft: true})
@post.update_attributes(draft: true)
end
if @post.save
@ -114,11 +116,11 @@ class PostsController < ApplicationController
obj.each do |key, value|
if value.is_a?(ActionController::Parameters) || value.is_a?(Hash)
# If any non-integer keys
if value.keys.find {|k, _| k =~ /\D/ }
if value.keys.find { |k, _| k =~ /\D/ }
repair_nested_params(value)
else
obj[key] = value.values
obj[key].each {|h| repair_nested_params(h) }
obj[key].each { |h| repair_nested_params(h) }
end
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Controlador de sitios
class SitesController < ApplicationController
include Pundit
@ -25,7 +27,7 @@ class SitesController < ApplicationController
file = [params[:basename], params[:format]].join('.')
path = Pathname.new(File.join(@site.path, 'public', params[:type], file))
# TODO Verificar que no nos estén sacando archivos del sistema, como
# TODO: Verificar que no nos estén sacando archivos del sistema, como
# /etc/passwd
#
# En sí el matcher de la ruta no admite .. así que estaríamos semi
@ -50,10 +52,10 @@ class SitesController < ApplicationController
@site = find_site
authorize @site
# TODO eliminar ANSI
# TODO: eliminar ANSI
render file: @site.build_log,
layout: false,
content_type: 'text/plain; charset=utf-8'
layout: false,
content_type: 'text/plain; charset=utf-8'
end
def reorder_posts
@ -61,11 +63,11 @@ class SitesController < ApplicationController
authorize @site
lang = params.require(:posts).require(:lang)
if params[:posts][:force].present?
result = @site.reorder_collection! lang
else
result = @site.reorder_collection(lang, params.require(:posts).require(:order))
end
result = if params[:posts][:force].present?
@site.reorder_collection! lang
else
@site.reorder_collection(lang, params.require(:posts).require(:order))
end
if result
flash[:info] = I18n.t('info.posts.reorder')

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
module ApplicationHelper
# Devuelve el atributo name de un campo posiblemente anidado
def field_name_for_post(names)

View file

@ -1,2 +1,4 @@
# frozen_string_literal: true
class ApplicationJob < ActiveJob::Base
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class InvitadxMailer < ApplicationMailer
def confirmation_required
@invitadx = params[:invitadx]

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Invitadx < ApplicationRecord
has_secure_password
validates_uniqueness_of :email
@ -15,9 +17,10 @@ class Invitadx < ApplicationRecord
File.join(Rails.root, '_invitadxs', email)
end
# TODO convertir el Pathname en un helper
# TODO: convertir el Pathname en un helper
def site_dirs
return [] unless Dir.exists? path
return [] unless Dir.exist? path
Pathname.new(path).children.map(&:expand_path).map(&:to_s)
end
@ -32,7 +35,7 @@ class Invitadx < ApplicationRecord
def acepto_politicas_de_privacidad
unless acepta_politicas_de_privacidad?
errors.add(:acepta_politicas_de_privacidad,
:no_acepta_politicas_de_privacidad)
:no_acepta_politicas_de_privacidad)
end
end
@ -47,11 +50,11 @@ class Invitadx < ApplicationRecord
dir = File.join(path, site.name)
file = site.invitadxs_file
unless File.exists? dir
unless File.exist? dir
FileUtils.ln_s File.join('..', '..', '_sites', site.name), dir
end
if File.exists? file
if File.exist? file
invitadxs = File.read(file).split("\n")
invitadxs_orig = invitadxs.dup
invitadxs << email unless invitadxs.include? email
@ -78,7 +81,6 @@ class Invitadx < ApplicationRecord
f.flush
f.truncate(f.pos)
end
end
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Esta clase se encarga de guardan los archivos YAML de idiomas
#
# TODO se podría convertir en una clase genérica de editor de YAML
@ -45,7 +47,7 @@ class JekyllI18n
# Obtiene la ruta a partir del sitio y el idioma
def path
File.join(@site.path, '_data', "#{@lang.to_s}.yml")
File.join(@site.path, '_data', "#{@lang}.yml")
end
def exist?
@ -79,6 +81,7 @@ class JekyllI18n
end
return true if r.zero?
false
end

View file

@ -1,4 +1,5 @@
# frozen_string_literal: true
require 'jekyll/utils'
# Esta clase representa un post en un sitio jekyll e incluye métodos
@ -16,7 +17,7 @@ require 'jekyll/utils'
class Post
attr_accessor :content, :front_matter
attr_reader :post, :site, :errors, :old_post, :lang, :template,
:template_fields
:template_fields
REJECT_FROM_DATA = %w[excerpt].freeze
# datos que no tienen que terminar en el front matter
@ -24,8 +25,8 @@ class Post
# datos que no traemos del template
REJECT_FROM_TEMPLATE = %w[draft categories layout title ext tags date slug post pre].freeze
DEFAULT_PARAMS = [:title, :date, :content, :slug, :cover,
:layout, :permalink, :dir,
{ lang: {} }, { tags: [] }, { categories: [] }]
:layout, :permalink, :dir,
{ lang: {} }, { tags: [] }, { categories: [] }].freeze
def inspect
"#<Post @id=#{id} @site=#{site.name}>"
@ -37,29 +38,29 @@ class Post
def initialize(site:, post: nil, front_matter: {}, lang: nil, template: nil)
unless site.is_a?(Site)
raise ArgumentError,
I18n.t('errors.argument_error', argument: :site, class: Site)
I18n.t('errors.argument_error', argument: :site, class: Site)
end
unless post.nil? || post.is_a?(Jekyll::Document)
raise ArgumentError,
I18n.t('errors.argument_error', argument: :post,
class: Jekyll::Document)
I18n.t('errors.argument_error', argument: :post,
class: Jekyll::Document)
end
@site = site
@post = post
@template = template
# los errores tienen que ser un hash para que
# los errores tienen que ser un hash para que
# ActiveModel pueda traer los errores normalmente
@errors = {}
# Si el sitio está traducido, trabajamos con la colección del
# idioma, sino, con posts
if @site.i18n?
@collection = @lang = lang || I18n.locale.to_s
else
@collection = 'posts'
end
@collection = if @site.i18n?
@lang = lang || I18n.locale.to_s
else
'posts'
end
# sincronizar los datos del document
if new?
@ -115,6 +116,7 @@ class Post
slugs.map do |lang, id|
next if lang == @lang
@site.posts_for(lang).find do |p|
p.id == id
end
@ -141,7 +143,7 @@ class Post
add_post_to_site!
true
end
alias :save! :save
alias save! save
def title
get_front_matter 'title'
@ -166,7 +168,7 @@ class Post
def categories
get_front_matter('categories') || []
end
alias :category :categories
alias category categories
# Devuelve la ruta del post, si se cambió alguno de los datos,
# generamos una ruta nueva para tener siempre la ruta actualizada.
@ -178,13 +180,13 @@ class Post
end
end
# TODO los slugs se pueden repetir, el identificador real sería
# TODO: los slugs se pueden repetir, el identificador real sería
# fecha+slug, pero se ve feo en las urls?
def id
get_front_matter 'slug'
end
alias :slug :id
alias :to_s :id
alias slug id
alias to_s id
def basename_changed?
@post.try(:basename) != basename_from_front_matter
@ -208,7 +210,7 @@ class Post
def has_field?(field)
if template
template.fetch_front_matter("has_#{field.to_s}", true)
template.fetch_front_matter("has_#{field}", true)
else
true
end
@ -225,7 +227,7 @@ class Post
# convertir los hashes en arrays si los campos son anidados
# usamos to_hash por todos lados porque sino son
# HashWithIndifferentAccess
_attrs = attrs.to_hash.map do |k,v|
_attrs = attrs.to_hash.map do |k, v|
t = template_fields.find { |t| t.key == k }
if t
# Subir la imagen!
@ -257,7 +259,7 @@ class Post
else
{ k => v }
end
end.reduce(Hash.new, :merge).stringify_keys
end.reduce({}, :merge).stringify_keys
# el cuerpo se maneja por separado
@content = _attrs.delete('content') if _attrs.key? 'content'
@ -275,6 +277,7 @@ class Post
# XXX este es un principio de validación de plantillas, aunque no es
# recursivo
return if fetch_front_matter('incomplete', false)
template_fields.each do |tf|
errors = [get_front_matter(tf.key)].flatten.compact
if tf.image? && errors.map { |i| File.exist?(File.join(site.path, i)) }.none?
@ -299,6 +302,7 @@ class Post
def url?(name)
path = get_front_matter(name)
return false unless path.is_a?(String) || path.is_a?(Array)
# El primer valor es '' porque la URL empieza con /
[path].flatten.map do |p|
p.split('/').second == 'public'
@ -308,20 +312,20 @@ class Post
def image?(name)
return false unless url? name
# TODO no chequear por la extensión
%[gif jpg jpeg png].include? get_front_matter(name).gsub(/.*\./, '')
# TODO: no chequear por la extensión
%(gif jpg jpeg png).include? get_front_matter(name).gsub(/.*\./, '')
end
# Obtiene metadatos de forma recursiva
# TODO devolver un valor por defecto en base al template?
def get_front_matter(name)
if name.is_a? Array
# Convertir los indices numericos a integers
name = name.map { |i| i =~ /[0-9]+/ ? i.to_i : i }
else
# XXX retrocompatibilidad
name = name.to_s
end
name = if name.is_a? Array
# Convertir los indices numericos a integers
name.map { |i| /[0-9]+/.match?(i) ? i.to_i : i }
else
# XXX retrocompatibilidad
name.to_s
end
@front_matter.dig(*name)
end
@ -341,9 +345,10 @@ class Post
end
end
# TODO convertir a hash para que sea más fácil buscar uno
# TODO: convertir a hash para que sea más fácil buscar uno
def template_fields
return [] unless template
@template_fields ||= template.front_matter.map do |key, contents|
next if REJECT_FROM_TEMPLATE.include? key
next if key.start_with? 'has_'
@ -355,9 +360,7 @@ class Post
# devuelve las plantillas como strong params, primero los valores
# simples, luego los arrays y al final los hashes
def template_params
@template_params ||= (DEFAULT_PARAMS + template_fields.map do |k|
k.to_param
end).sort_by do |s|
@template_params ||= (DEFAULT_PARAMS + template_fields.map(&:to_param)).sort_by do |s|
s.is_a?(Symbol) ? 0 : 1
end
end
@ -375,7 +378,7 @@ class Post
# entramos en una race condition
return {} unless @template
ft = template_fields.map(&:to_front_matter).reduce(Hash.new, :merge)
ft = template_fields.map(&:to_front_matter).reduce({}, :merge)
# Convertimos el slug en layout
ft['layout'] = template.slug
@ -430,7 +433,7 @@ class Post
# Obtiene el nombre del archivo a partir de los datos que le
# pasemos
def basename_from_front_matter
ext = get_front_matter('ext') || '.markdown'
ext = get_front_matter('ext') || '.markdown'
"#{date_as_string}-#{slug}#{ext}"
end
@ -485,7 +488,7 @@ class Post
end
def remove_empty_front_matter!
@front_matter.delete_if do |k,v|
@front_matter.delete_if do |_k, v|
v.blank?
end
end
@ -526,7 +529,7 @@ class Post
# obtener el contenido por defecto si es que no lo enviamos
# modificaciones, como en `update_translations!`
def full_content
yaml = @front_matter.reject do |k,_|
yaml = @front_matter.reject do |k, _|
REJECT_FROM_FRONT_MATTER.include? k
end
@ -534,12 +537,12 @@ class Post
end
def add_error(hash)
hash.each_pair do |k,i|
if @errors.key?(k)
@errors[k] = [@errors[k], i]
else
@errors[k] = i
end
hash.each_pair do |k, i|
@errors[k] = if @errors.key?(k)
[@errors[k], i]
else
i
end
end
@errors
@ -557,18 +560,17 @@ class Post
# XXX es necesario ahora que tenemos select2?
def things_to_arrays!
[:tags,:categories].each do |c|
%i[tags categories].each do |c|
thing = @front_matter.dig(c.to_s)
next if thing.blank?
next if thing.is_a? Array
@front_matter[c.to_s] = thing.split(',').map(&:strip)
end
end
def slugify_title!
if slug.blank?
@front_matter['slug'] = Jekyll::Utils.slugify(title)
end
@front_matter['slug'] = Jekyll::Utils.slugify(title) if slug.blank?
end
# Agregar al final de la cola si no especificamos un orden
@ -585,20 +587,20 @@ class Post
def create_glossary!
return unless site.glossary?
[:tags,:categories].each do |i|
self.send(i).each do |c|
# TODO no hardcodear, hacer _configurable
%i[tags categories].each do |i|
send(i).each do |c|
# TODO: no hardcodear, hacer _configurable
next if c == 'Glossary'
next if site.posts.find do |p|
p.title == c
end
glossary = Post.new(site: site, lang: lang)
glossary.update_attributes({
glossary.update_attributes(
title: c,
layout: 'glossary',
categories: 'Glossary'
})
)
glossary.save!
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Una clase que permite adjuntar imágenes a artículos
class Post::ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
@ -9,7 +11,7 @@ class Post::ImageUploader < CarrierWave::Uploader::Base
version :thumb do
process :strip
process resize_to_fit: [200,200]
process resize_to_fit: [200, 200]
end
def strip
@ -28,7 +30,7 @@ class Post::ImageUploader < CarrierWave::Uploader::Base
# Solo aceptamos imágenes
def content_type_whitelist
/\Aimage\//
%r{\Aimage/}
end
# Devuelve la ubicación del directorio dentro de Jekyll, para eso,
@ -46,10 +48,10 @@ class Post::ImageUploader < CarrierWave::Uploader::Base
# están repetidos.
def filename
current_filename = parent_version.try(:filename) || file.try(:filename)
@unique_filename ||= if /\A(thumb_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\..*\Z/ =~ current_filename
current_filename
else
[SecureRandom.uuid, '.', file.extension].join
@unique_filename ||= if /\A(thumb_)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\..*\Z/.match?(current_filename)
current_filename
else
[SecureRandom.uuid, '.', file.extension].join
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Representa los distintos tipos de campos que pueden venir de una
# plantilla compleja
class Post
@ -5,7 +7,7 @@ class Post
attr_reader :post, :contents, :key
STRING_VALUES = %w[string text url number email password date year
image video audio document].freeze
image video audio document].freeze
# Tipo de valores que son archivos
FILE_TYPES = %w[image video audio document].freeze
@ -27,50 +29,51 @@ class Post
def max
return 0 if simple?
contents.fetch('max', 0)
end
def min
return 0 if simple?
contents.fetch('min', 0)
end
# TODO volver elegante!
# TODO: volver elegante!
def type
return @type if @type
case
when image?
if image?
@type = 'image'
when email?
elsif email?
@type = 'email'
when url?
elsif url?
@type = 'url'
when number?
elsif number?
@type = 'number'
when password?
elsif password?
@type = 'password'
when date?
elsif date?
@type = 'date'
when year?
elsif year?
@type = 'year'
when text_area?
elsif text_area?
@type = 'text_area'
when check_box_group?
elsif check_box_group?
@type = 'check_box_group'
when radio_group?
elsif radio_group?
@type = 'radio_group'
when string?
elsif string?
@type = 'text'
# TODO volver a hacer funcionar esto y ahorranos los multiple:
# TODO: volver a hacer funcionar esto y ahorranos los multiple:
# false
when string? && contents.split('/', 2).count == 2
elsif string? && contents.split('/', 2).count == 2
@type = 'select'
when nested?
elsif nested?
@type = 'table'
when array?
elsif array?
@type = 'select'
when boolean?
elsif boolean?
@type = 'check_box'
end
@ -79,15 +82,14 @@ class Post
# Devuelve los valores vacíos según el tipo
def empty_value
case
when string?
if string?
''
when nested?
# TODO devolver las keys también
elsif nested?
# TODO: devolver las keys también
{}
when array?
elsif array?
[]
when boolean?
elsif boolean?
false
end
end
@ -124,7 +126,7 @@ class Post
def email?
value == 'email' || value == 'mail'
end
alias :mail? :email?
alias mail? email?
def date?
value == 'date'
@ -192,7 +194,7 @@ class Post
value.is_a? Array
end
# TODO detectar cuando es complejo y tomar el valor de :multiple
# TODO: detectar cuando es complejo y tomar el valor de :multiple
def multiple?
# si la plantilla es simple, es multiple cuando tenemos un array
return array? if simple?
@ -211,6 +213,7 @@ class Post
# Todos los valores simples son abiertos
return true unless complex?
return false unless array?
# La cosa se complejiza cuando tenemos valores complejos
#
# Si tenemos una lista cerrada de valores, necesitamos saber si el
@ -241,7 +244,7 @@ class Post
def human
h = key.humanize
h = h + ' *' if required?
h += ' *' if required?
h
end
@ -260,21 +263,21 @@ class Post
v = value
v = value.first if array?
@nested_fields ||= v.map do |k,sv|
@nested_fields ||= v.map do |k, sv|
Post::TemplateField.new post, k, sv
end
end
# Obtiene los valores posibles para el campo de la plantilla
def values
return 'false' if self.value == false
return 'true' if self.value == true
return 'false' if value == false
return 'true' if value == true
# XXX por alguna razón `value` no refiere a value() :/
return '' if STRING_VALUES.include? self.value
return '' if STRING_VALUES.include? value
# Las listas cerradas no necesitan mayor procesamiento
return self.value if array? && closed? && self.value.count > 1
return value if array? && closed? && value.count > 1
# Y las vacías tampoco
return self.value if array? && self.value.empty?
return value if array? && value.empty?
# Ahorrarnos el trabajo
return @values if @values
@ -301,7 +304,7 @@ class Post
collection, attr, subattr = v.split('/', 3)
if collection == 'site'
# TODO puede ser peligroso permitir acceder a cualquier
# TODO: puede ser peligroso permitir acceder a cualquier
# atributo de site? No estamos trayendo nada fuera de
# lo normal
post.site.send(attr.to_sym)
@ -311,11 +314,11 @@ class Post
# TODO volver recursivo!
elsif subattr
post.site.everything_of(attr, lang: collection)
.compact
.map { |sv| sv[subattr] }
.flatten
.compact
.uniq
.compact
.map { |sv| sv[subattr] }
.flatten
.compact
.uniq
else
post.site.everything_of(attr, lang: collection).compact
end
@ -331,7 +334,7 @@ class Post
if _value.count == 1
_value = [(_value.first + value).uniq]
elsif _value.count == 2
_value = _value.each_with_index.map do |v,i|
_value = _value.each_with_index.map do |v, i|
v + value.fetch(i, [])
end
end

View file

@ -1,7 +1,8 @@
# frozen_string_literal: true
# Un sitio es un directorio dentro del directorio de sitios de cada
# Usuaria
class Site
attr_accessor :jekyll, :collections
attr_reader :path
@ -71,8 +72,8 @@ class Site
def name
@name ||= File.basename(@jekyll.config['source'])
end
alias :id :name
alias :to_s :name
alias id name
alias to_s name
def name_with_i18n(lang)
[name, lang].join('/')
@ -108,9 +109,7 @@ class Site
@jekyll.config
end
def collections
@collections
end
attr_reader :collections
def collections_names
@jekyll.config['collections'].keys
@ -132,6 +131,7 @@ class Site
# Devuelve nil si la colección no existe
def posts_for(collection)
return unless collections_names.include? collection
# Si pedimos 'posts' pero estamos en un sitio traducido, traemos el
# idioma actual
collection = default_lang if collection == 'posts' && i18n?
@ -192,6 +192,7 @@ class Site
def invitadxs
return [] unless invitadxs?
@invitadxs ||= File.read(invitadxs_file).split("\n").map do |i|
Invitadx.find_by_email(i)
end
@ -208,7 +209,7 @@ class Site
def defail
FileUtils.rm failed_file if failed?
end
alias :defail! :defail
alias defail! defail
def build_log
File.join(path, 'build.log')
@ -225,13 +226,13 @@ class Site
def enqueued?
File.exist? queue_file
end
alias :queued? :enqueued?
alias queued? enqueued?
# El sitio se genera cuando se coloca en una cola de generación, para
# que luego lo construya un cronjob
def enqueue
defail!
# TODO ya van tres métodos donde usamos esta idea, convertir en un
# TODO: ya van tres métodos donde usamos esta idea, convertir en un
# helper o algo
r = File.open(queue_file, File::RDWR | File::CREAT, 0o640) do |f|
# Bloquear el archivo para que no sea accedido por otro
@ -249,19 +250,17 @@ class Site
f.truncate(f.pos)
end
end
alias :enqueue! :enqueue
alias enqueue! enqueue
# Eliminar de la cola
def dequeue
FileUtils.rm(queue_file) if enqueued?
end
alias :dequeue! :dequeue
alias dequeue! dequeue
# Verifica si los posts están ordenados
def ordered?(collection = 'posts')
posts_for(collection).map do |p|
p.order
end.all?
posts_for(collection).map(&:order).all?
end
# Reordena la colección usando la posición informada
@ -293,10 +292,10 @@ class Site
# Reordena la colección usando la posición actual de los artículos
def reorder_collection!(collection = 'posts')
order = Hash[posts_for(collection).count.times.map { |i| [i.to_s,i.to_s] }]
order = Hash[posts_for(collection).count.times.map { |i| [i.to_s, i.to_s] }]
reorder_collection collection, order
end
alias :reorder_posts! :reorder_collection!
alias reorder_posts! reorder_collection!
# Obtener una ruta disponible para Sutty
def get_url_for_sutty(path)
@ -336,9 +335,9 @@ class Site
# Especificamos `safe` para no cargar los _plugins, que interfieren
# entre sitios incluso
config = ::Jekyll.configuration('source' => path,
'destination' => File.join(path, '_site'),
'safe' => true,
'watch' => false)
'destination' => File.join(path, '_site'),
'safe' => true,
'watch' => false)
# No necesitamos cargar plugins en este momento
%w[plugins gems theme].each do |unneeded|
@ -348,10 +347,8 @@ class Site
# Si estamos usando nuestro propio plugin de i18n, los posts están
# en "colecciones"
i18n = config.dig('i18n')
if i18n
i18n.each do |i|
config['collections'][i] = {}
end
i18n&.each do |i|
config['collections'][i] = {}
end
Jekyll::Site.new(config)

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Un modelo para la usuaria que no se corresponde con una base de datos
# porque vienen de IMAP
#

View file

@ -1,6 +1,7 @@
class I18nPolicy < SuttyPolicy
# frozen_string_literal: true
def initialize(usuarix, i18n)
class I18nPolicy < SuttyPolicy
def initialize(usuarix, _i18n)
@usuarix = usuarix
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class InvitadxPolicy
attr_reader :usuarix, :model

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class PostPolicy < SuttyPolicy
attr_reader :post

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SitePolicy < SuttyPolicy
attr_reader :site

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class SuttyPolicy
attr_reader :usuarix

View file

@ -117,7 +117,7 @@
- @post.template_fields.each do |template|
- next unless type = template.type
- if template.title.present?
%h1= template.title
%h1{id: template.title.tr(' ', '_').titleize}= template.title
- value = @post.new? ? template.values : @post.get_front_matter(template.key)
.form-group
= label_tag "post_#{template}", id: template do

View file

@ -8,22 +8,23 @@
# this file is here to facilitate running it.
#
require "rubygems"
require 'rubygems'
m = Module.new do
module_function
module_function
def invoked_as_script?
File.expand_path($0) == File.expand_path(__FILE__)
File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
end
def env_var_version
ENV["BUNDLER_VERSION"]
ENV['BUNDLER_VERSION']
end
def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
@ -31,23 +32,24 @@ m = Module.new do
bundler_version = a
end
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
bundler_version = $1 || ">= 0.a"
bundler_version = Regexp.last_match(1) || '>= 0.a'
update_index = i
end
bundler_version
end
def gemfile
gemfile = ENV["BUNDLE_GEMFILE"]
gemfile = ENV['BUNDLE_GEMFILE']
return gemfile if gemfile && !gemfile.empty?
File.expand_path("../../Gemfile", __FILE__)
File.expand_path('../Gemfile', __dir__)
end
def lockfile
lockfile =
case File.basename(gemfile)
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
@ -55,8 +57,10 @@ m = Module.new do
def lockfile_version
return unless File.file?(lockfile)
lockfile_contents = File.read(lockfile)
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
Regexp.last_match(1)
end
@ -68,24 +72,26 @@ m = Module.new do
end
def load_bundler!
ENV["BUNDLE_GEMFILE"] ||= gemfile
ENV['BUNDLE_GEMFILE'] ||= gemfile
# must dup string for RG < 1.8 compatibility
activate_bundler(bundler_version.dup)
end
def activate_bundler(bundler_version)
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
bundler_version = "< 2"
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new('2.0')
bundler_version = '< 2'
end
gem_error = activation_error_handling do
gem "bundler", bundler_version
gem 'bundler', bundler_version
end
return if gem_error.nil?
require_error = activation_error_handling do
require "bundler/version"
require 'bundler/version'
end
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
exit 42
end
@ -100,6 +106,4 @@ end
m.load_bundler!
if m.invoked_as_script?
load Gem.bin_path("bundler", "bundle")
end
load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?

16
bin/cap
View file

@ -8,14 +8,14 @@
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path("../bundle", __FILE__)
bundle_binstub = File.expand_path('bundle', __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@ -23,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
end
end
require "rubygems"
require "bundler/setup"
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path("capistrano", "cap")
load Gem.bin_path('capistrano', 'cap')

View file

@ -8,14 +8,14 @@
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path("../bundle", __FILE__)
bundle_binstub = File.expand_path('bundle', __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@ -23,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
end
end
require "rubygems"
require "bundler/setup"
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path("capistrano", "capify")
load Gem.bin_path('capistrano', 'capify')

View file

@ -1,6 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
begin
load File.expand_path('../spring', __FILE__)
load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end

View file

@ -1,6 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
begin
load File.expand_path('../spring', __FILE__)
load File.expand_path('spring', __dir__)
rescue LoadError => e
raise unless e.message.include?('spring')
end

View file

@ -1,10 +1,12 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
APP_ROOT = Pathname.new File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
@ -21,7 +23,6 @@ chdir APP_ROOT do
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'

View file

@ -1,4 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
# This file loads spring without using Bundler, in order to be fast.
# It gets overwritten when you run the `spring binstub` command.
@ -8,7 +9,7 @@ unless defined?(Spring)
require 'bundler'
lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
spring = lockfile.specs.detect { |spec| spec.name == "spring" }
spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
if spring
Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
gem 'spring', spring.version

View file

@ -1,10 +1,12 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
APP_ROOT = Pathname.new File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")

View file

@ -8,14 +8,14 @@
# this file is here to facilitate running it.
#
bundle_binstub = File.expand_path("../bundle", __FILE__)
bundle_binstub = File.expand_path('bundle', __dir__)
load(bundle_binstub) if File.file?(bundle_binstub)
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path("whenever", "whenever")
load Gem.bin_path('whenever', 'whenever')

View file

@ -8,14 +8,14 @@
# this file is here to facilitate running it.
#
bundle_binstub = File.expand_path("../bundle", __FILE__)
bundle_binstub = File.expand_path('bundle', __dir__)
load(bundle_binstub) if File.file?(bundle_binstub)
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path("whenever", "wheneverize")
load Gem.bin_path('whenever', 'wheneverize')

View file

@ -1,11 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
VENDOR_PATH = File.expand_path('..', __dir__)
Dir.chdir(VENDOR_PATH) do
begin
exec "yarnpkg #{ARGV.join(" ")}"
rescue Errno::ENOENT
$stderr.puts "Yarn executable was not detected in the system."
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1
end
exec "yarnpkg #{ARGV.join(' ')}"
rescue Errno::ENOENT
warn 'Yarn executable was not detected in the system.'
warn 'Download Yarn at https://yarnpkg.com/en/docs/install'
exit 1
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'

View file

@ -1,16 +1,18 @@
# frozen_string_literal: true
require_relative 'boot'
require "rails"
require 'rails'
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require 'active_model/railtie'
require 'active_job/railtie'
require 'active_record/railtie'
require 'action_controller/railtie'
require 'action_mailer/railtie'
require 'action_view/railtie'
# require "action_cable/engine"
require "sprockets/railtie"
require "rails/test_unit/railtie"
require 'sprockets/railtie'
require 'rails/test_unit/railtie'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.

View file

@ -1,11 +1,12 @@
# frozen_string_literal: true
set :application, 'sutty'
set :repo_url, 'git@0xacab.org:itacate-kefir/sutty.git'
set :bundle_flags, '--deployment'
set :default_env, path: '/usr/lib/passenger/bin:$PATH'
set :linked_dirs, %w{_sites _deploy _usuarias _invitadxs public/covers}
set :linked_files, %w{.env db/production.sqlite3}
set :linked_dirs, %w[_sites _deploy _usuarias _invitadxs public/covers]
set :linked_files, %w[.env db/production.sqlite3]
set :keep_releases, 1

View file

@ -1,4 +1,5 @@
# frozen_string_literal: true
set :deploy_user, 'DEPLOY_USER'
set :deploy_to, '/srv/http/sutty.kefir.red'
set :branch, 'rails'
@ -11,7 +12,7 @@ set :rbenv_type, :user
set :rbenv_ruby, '2.3.6'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w(rake gem bundle ruby rails)
set :rbenv_map_bins, %w[rake gem bundle ruby rails]
set :rbenv_roles, :all # default value
# Evitar compilar nokogiri
@ -19,4 +20,4 @@ set :bundle_env_variables, nokogiri_use_system_libraries: 1
server 'DEPLOY_SERVER',
user: fetch(:deploy_user),
roles: %w(app web db)
roles: %w[app web db]

View file

@ -1,4 +1,5 @@
# frozen_string_literal: true
set :deploy_user, 'app'
set :deploy_to, '/srv/http/sutty.kefir.red'
set :branch, 'rails'
@ -11,7 +12,7 @@ set :rbenv_type, :user
set :rbenv_ruby, '2.3.6'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w(rake gem bundle ruby rails)
set :rbenv_map_bins, %w[rake gem bundle ruby rails]
set :rbenv_roles, :all # default value
# Evitar compilar nokogiri
@ -19,4 +20,4 @@ set :bundle_env_variables, nokogiri_use_system_libraries: 1
server 'miso',
user: fetch(:deploy_user),
roles: %w(app web db)
roles: %w[app web db]

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Load the Rails application.
require_relative 'application'

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
@ -39,7 +41,6 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
@ -48,7 +49,7 @@ Rails.application.configure do
config.log_level = :debug
# Prepend all log lines with the following tags.
config.log_tags = [ :request_id ]
config.log_tags = [:request_id]
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
@ -76,7 +77,7 @@ Rails.application.configure do
# require 'syslog/logger'
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
if ENV["RAILS_LOG_TO_STDOUT"].present?
if ENV['RAILS_LOG_TO_STDOUT'].present?
logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
@ -93,9 +94,9 @@ Rails.application.configure do
config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', 'sutty@kefir.red') }
config.middleware.use ExceptionNotification::Rack,
email: {
email_prefix: '[ERROR]',
sender_address: %(sutty@kefir.red),
exception_recipients: 'sysadmin@kefir.red'
}
email: {
email_prefix: '[ERROR]',
sender_address: %(sutty@kefir.red),
exception_recipients: 'sysadmin@kefir.red'
}
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# ActiveSupport::Reloader.to_prepare do

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
CarrierWave.configure do |config|
config.permissions = 0640
config.directory_permissions = 0750
config.permissions = 0o640
config.directory_permissions = 0o750
config.storage = :file
end

View file

@ -1,18 +1,17 @@
# frozen_string_literal: true
require 'commonmarker'
module Haml::Filters
remove_filter("Markdown") #remove the existing Markdown filter
remove_filter('Markdown') # remove the existing Markdown filter
module Markdown
include Haml::Filters::Base
def render(text)
CommonMarker.render_html(text,
[:TABLE_PREFER_STYLE_ATTRIBUTES],
[:table,:strikethrough,:autolink,:tagfilter])
[:TABLE_PREFER_STYLE_ATTRIBUTES],
%i[table strikethrough autolink tagfilter])
end
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Specify a serializer for the signed and encrypted cookie jars.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.plural 'invitadx', 'invitadxs'
inflect.singular 'invitadxs', 'invitadx'

View file

@ -1,4 +1,6 @@
# frozen_string_literal: true
Rails.application.configure do
config.i18n.available_locales = [:es, :en, :ar]
config.i18n.available_locales = %i[es en ar]
config.i18n.default_locale = :es
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Add new mime types for use in respond_to blocks:

View file

@ -1,9 +1,11 @@
# frozen_string_literal: true
require 'warden/imap'
require 'warden/email_and_password'
Rails.configuration.middleware.use RailsWarden::Manager do |manager|
manager.default_strategies :email, :imap
manager.failure_app = -> (env) { LoginController.action(:new).call(env) }
manager.failure_app = ->(env) { LoginController.action(:new).call(env) }
end
class Warden::SessionSerializer

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# This file contains settings for ActionController::ParamsWrapper which

View file

@ -1,19 +1,21 @@
# frozen_string_literal: true
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 1 }
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }
port ENV.fetch('PORT') { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
environment ENV.fetch('RAILS_ENV') { 'development' }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
Rails.application.routes.draw do
root 'application#index'
@ -7,16 +9,15 @@ Rails.application.routes.draw do
get 'markdown', to: 'application#markdown'
# XXX Mantenemos solo esta ruta acá porque form_for no reconoce @site
# como un objeto válido
resources :invitadxs, only: [:create]
resources :sites, only: [:index, :show], constraints: { site_id: /[^\/]+/, id: /[^\/]+/ } do
resources :sites, only: %i[index show], constraints: { site_id: %r{[^/]+}, id: %r{[^/]+} } do
get 'public/:type/:basename', to: 'sites#send_public_file'
resources :posts
resources :templates
resources :invitadxs, only: [:index, :new, :show] do
resources :invitadxs, only: %i[index new show] do
get :confirmation, to: 'invitadxs#confirmation'
end
get :'invitadxs/login/new', to: 'login#new'

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
env 'MAILTO', 'sysadmin@kefir.red'
job_type :bash, 'cd :path && ./bin/:task'

View file

@ -1,14 +1,16 @@
# frozen_string_literal: true
# https://github.com/thoughtbot/ember-cli-rails/issues/479#issuecomment-241302961
Spring::Watcher::Listen.class_eval do
def base_directories
%w(app config db lib test vendor)
%w[app config db lib test vendor]
.uniq.map { |path| Pathname.new(File.join(root, path)) }
end
end
%w(
%w[
.ruby-version
.rbenv-vars
tmp/restart.txt
tmp/caching-dev.txt
).each { |path| Spring.watch(path) }
].each { |path| Spring.watch(path) }

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class CreateInvitadxs < ActiveRecord::Migration[5.1]
def change
create_table :invitadxs do |t|

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AddAceptaPoliticasDePrivacidadToInvitadx < ActiveRecord::Migration[5.1]
def change
add_column :invitadxs, :acepta_politicas_de_privacidad, :boolean, default: false

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
#

View file

@ -1,11 +1,13 @@
# frozen_string_literal: true
require 'email_address'
module Warden
module EmailAndPassword
class Strategy < Warden::Strategies::Base
def valid?
return false unless params.include? 'username'
return false unless params.include? 'password'
username = params['username']
@email = EmailAddress.new(username, host_validation: :a)

View file

@ -55,4 +55,3 @@ module Warden
end
end
end

View file

@ -1,4 +1,6 @@
require "test_helper"
# frozen_string_literal: true
require 'test_helper'
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class PostTest < ActiveSupport::TestCase

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class SiteTest < ActiveSupport::TestCase
@ -9,7 +11,7 @@ class SiteTest < ActiveSupport::TestCase
@site.read
end
test "El directorio es un sitio jekyll" do
test 'El directorio es un sitio jekyll' do
assert Site.jekyll?(@path)
end
@ -27,21 +29,21 @@ class SiteTest < ActiveSupport::TestCase
test 'No podemos poner órdenes arbitrarios' do
total = @site.posts.count
new_order = Hash[total.times.map { |i| [i.to_s,rand(total)] }]
new_order = Hash[total.times.map { |i| [i.to_s, rand(total)] }]
assert_not @site.reorder_collection('posts', new_order)
end
test 'Si les damos un orden alternativo los reordenamos' do
total = @site.posts.count
order = total.times.map { |i| i.to_s }
order = total.times.map(&:to_s)
new_order = Hash[order.zip(order.shuffle)]
assert @site.reorder_collection('posts', new_order)
# podemos hacer este test porque reordenar los posts no ordena el
# array
new_order.each do |k,v|
new_order.each do |k, v|
v = v.to_i
k = k.to_i
assert_equal v, @site.posts[k].order
@ -50,14 +52,14 @@ class SiteTest < ActiveSupport::TestCase
test 'Podemos reordenar solo una parte de los artículos' do
total = @site.posts.count
order = (total - rand(total - 1)).times.map { |i| i.to_s }
order = (total - rand(total - 1)).times.map(&:to_s)
new_order = Hash[order.zip(order.shuffle)]
assert @site.reorder_collection('posts', new_order)
# podemos hacer este test porque reordenar los posts no ordena el
# array
new_order.each do |k,v|
new_order.each do |k, v|
v = v.to_i
k = k.to_i
assert_equal v, @site.posts[k].order
@ -75,7 +77,7 @@ class SiteTest < ActiveSupport::TestCase
test 'El sitio tiene layouts' do
assert_equal %w[anexo archive default feed header.ar header.en
header.es header license.ar license.en license.es license pandoc
politicas sesion simple style ].sort, @site.layouts
header.es header license.ar license.en license.es license pandoc
politicas sesion simple style ].sort, @site.layouts
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'test_helper'
class UsuariaTest < ActiveSupport::TestCase
@ -6,11 +8,11 @@ class UsuariaTest < ActiveSupport::TestCase
@usuaria = Usuaria.find(@mail)
end
test "La usuaria puede encontrarse por su mail" do
test 'La usuaria puede encontrarse por su mail' do
assert_equal @mail, @usuaria.username
end
test "La usuaria tiene sitios" do
test 'La usuaria tiene sitios' do
@usuaria.sites.each do |s|
assert_equal Site, s.class
end

View file

@ -1,4 +1,6 @@
require File.expand_path('../../config/environment', __FILE__)
# frozen_string_literal: true
require File.expand_path('../config/environment', __dir__)
require 'rails/test_help'
require 'open3'
@ -6,7 +8,7 @@ class ActiveSupport::TestCase
# Resetear el repositorio a su estado original antes de leerlo
def reset_git_repo(path)
Dir.chdir(path) do
Open3::popen3('git reset --hard') do |_, _, _, thread|
Open3.popen3('git reset --hard') do |_, _, _, thread|
# Wait for the process to finish
thread.value
end