diff --git a/.rubocop.yml b/.rubocop.yml index 5e07d37..eb26118 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -42,6 +42,7 @@ Metrics/BlockLength: - 'config/initializers/devise.rb' - 'db/schema.rb' - 'config/routes.rb' + - 'test/controllers/sites_controller_test.rb' Metrics/ClassLength: Exclude: @@ -49,6 +50,7 @@ Metrics/ClassLength: - 'app/controllers/posts_controller.rb' - 'app/controllers/sites_controller.rb' - 'test/models/post_test.rb' + - 'test/controllers/sites_controller_test.rb' Lint/HandleExceptions: Exclude: diff --git a/app/controllers/sites_controller.rb b/app/controllers/sites_controller.rb index 527762a..95cf019 100644 --- a/app/controllers/sites_controller.rb +++ b/app/controllers/sites_controller.rb @@ -29,15 +29,10 @@ class SitesController < ApplicationController end def create - @site = Site.new(site_params) - @site.roles << Rol.new(site: @site, - usuarie: current_usuarie, - temporal: false, - rol: 'usuarie') + service = SiteService.new(usuarie: current_usuarie, + params: site_params) - # XXX: Necesitamos escribir la configuración después porque queremos - # registrar quién hizo los cambios en el repositorio - if @site.save && @site.config.write(current_usuarie) + if (@site = service.create).persisted? redirect_to site_path(@site) else render 'new' @@ -53,9 +48,10 @@ class SitesController < ApplicationController @site = find_site authorize @site - # XXX: Necesitamos escribir la configuración después porque queremos - # registrar quién hizo los cambios en el repositorio - if @site.update(site_params) && @site.config.write(current_usuarie) + service = SiteService.new(site: @site, params: site_params, + usuarie: current_usuarie) + + if service.update redirect_to sites_path else render 'edit' @@ -64,8 +60,9 @@ class SitesController < ApplicationController # Envía un archivo del directorio público de Jekyll def send_public_file - authorize Site @site = find_site + authorize @site + file = [params[:basename], params[:format]].join('.') path = File.join(@site.path, 'public', params[:type], file) path = Pathname.new path diff --git a/app/models/site.rb b/app/models/site.rb index 776a4b9..c4fc5fd 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -316,9 +316,9 @@ class Site < ApplicationRecord # TODO: Guardar la configuración también, quizás aprovechando algún # método de ActiveRecord para que lance un salvado recursivo. def sync_attributes_with_config! - config.theme = design.gem unless design_id_changed? - config.description = description unless description_changed? - config.title = title unless title_changed? + config.theme = design.gem + config.description = description + config.title = title end # Valida si el sitio tiene al menos una forma de alojamiento asociada diff --git a/app/models/site/config.rb b/app/models/site/config.rb index 8ed79d9..f4bf041 100644 --- a/app/models/site/config.rb +++ b/app/models/site/config.rb @@ -8,6 +8,8 @@ class Site # Iniciar el OpenStruct con el sitio super(site: site) + self.saved = File.exist? path + read end @@ -28,21 +30,19 @@ class Site end # Escribe los cambios en el repositorio - def write(usuarie = nil) + def write return if persisted? - I18n.with_locale(usuarie.try(:lang) || I18n.default_locale) do - Site::Writer.new(site: site, file: path, - content: content.to_yaml, usuarie: usuarie, - message: I18n.t('sites.repository.config')).save - end + self.saved = Site::Writer.new(site: site, file: path, + content: content.to_yaml).save # Actualizar el hash para no escribir dos veces @hash = content.hash end + alias save write # Detecta si la configuración cambió comparando con el valor inicial def persisted? - @hash == content.hash + (@hash == content.hash) && saved end # Obtener el contenido de la configuración como un hash, sin el diff --git a/app/models/site/repository.rb b/app/models/site/repository.rb index 729dad5..ac9fc22 100644 --- a/app/models/site/repository.rb +++ b/app/models/site/repository.rb @@ -6,9 +6,10 @@ class Site # que un sitio tiene un solo origen, que siempre se trabaja con la # rama master, etc. class Repository - attr_reader :rugged, :changes + attr_reader :rugged, :changes, :path def initialize(path) + @path = path @rugged = Rugged::Repository.new(path) @changes = 0 end @@ -87,7 +88,7 @@ class Site # Guarda los cambios en git, de a un archivo por vez # rubocop:disable Metrics/AbcSize def commit(file:, usuarie:, message:) - rugged.index.add(file) + rugged.index.add(relativize(file)) rugged.index.write Rugged::Commit.create(rugged, @@ -107,5 +108,11 @@ class Site def committer { name: 'Sutty', email: "sutty@#{Site.domain}", time: Time.now } end + + private + + def relativize(file) + Pathname.new(file).relative_path_from(Pathname.new(path)).to_s + end end end diff --git a/app/services/site_service.rb b/app/services/site_service.rb new file mode 100644 index 0000000..c475b72 --- /dev/null +++ b/app/services/site_service.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Se encargar de guardar cambios en sitios +# TODO: Implementar rollback en la configuración +# rubocop:disable Metrics/BlockLength +SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do + # Crea un sitio, agrega un rol nuevo y guarda los cambios a la + # configuración en el repositorio git + def create + self.site = Site.new params + + add_role temporal: false, rol: 'usuarie' + + I18n.with_locale(usuarie.try(:lang) || I18n.default_locale) do + site.save && + site.config.write && + commit_config(action: :create) + end + + site + end + + # Actualiza el sitio y guarda los cambios en la configuración + def update + I18n.with_locale(usuarie.try(:lang) || I18n.default_locale) do + site.update_attributes(params) && + site.config.write && + commit_config(action: :update) + end + + site + end + + private + + # Guarda los cambios de la configuración en el repositorio git + def commit_config(action:) + site.repository + .commit(usuarie: usuarie, + file: site.config.path, + message: I18n.t("site_service.#{action}", + name: site.name)) + end + + def add_role(temporal: true, rol: 'invitade') + site.roles << Rol.new(site: site, usuarie: usuarie, + temporal: temporal, rol: rol) + end +end +# rubocop:enable Metrics/BlockLength diff --git a/config/locales/en.yml b/config/locales/en.yml index 4d974f6..2c08ee1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,4 +1,7 @@ en: + site_service: + create: 'Created %{name}' + update: 'Updated %{name}' post_service: created: 'Created "%{title}"' metadata: diff --git a/config/locales/es.yml b/config/locales/es.yml index 6298d74..f7fe475 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1,4 +1,7 @@ es: + site_service: + create: 'Creado %{name}' + update: 'Actualizado %{name}' post_service: created: 'Creado "%{title}"' metadata: diff --git a/test/controllers/sites_controller_test.rb b/test/controllers/sites_controller_test.rb index 35646f3..3697a85 100644 --- a/test/controllers/sites_controller_test.rb +++ b/test/controllers/sites_controller_test.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'test_helper' + class SitesControllerTest < ActionDispatch::IntegrationTest setup do @rol = create :rol @@ -30,13 +32,14 @@ class SitesControllerTest < ActionDispatch::IntegrationTest test 'se pueden crear' do name = SecureRandom.hex + design = create :design post sites_url, headers: @authorization, params: { site: { name: name, title: name, description: name * 2, - design_id: create(:design).id, + design_id: design.id, licencia_id: create(:licencia).id, deploys_attributes: { '0' => { @@ -52,6 +55,16 @@ class SitesControllerTest < ActionDispatch::IntegrationTest assert_equal @usuarie.email, site.roles.first.usuarie.email assert_equal 'usuarie', site.roles.first.rol + assert_equal name, site.name + assert_equal name, site.title + assert_equal name * 2, site.description + assert_equal design, site.design + assert_equal design.gem, site.config.theme + assert_equal name, site.config.title + assert_equal name * 2, site.config.description + assert_equal I18n.t('site_service.create', name: name), + site.repository.rugged.head.target.message + site.destroy end @@ -84,4 +97,36 @@ class SitesControllerTest < ActionDispatch::IntegrationTest Sidekiq::Testing.inline! end + + test 'se pueden actualizar' do + name = SecureRandom.hex + design = create :design + + put site_url(@site), headers: @authorization, params: { + site: { + name: name, + title: name, + description: name * 2, + design_id: design.id, + licencia_id: create(:licencia).id, + deploys_attributes: { + '0' => { + type: 'DeployLocal' + } + } + } + } + + @site = Site.find(@site.id) + + assert_equal name, @site.name + assert_equal name, @site.title + assert_equal name * 2, @site.description + assert_equal design, @site.design + assert_equal design.gem, @site.config.theme + assert_equal name, @site.config.title + assert_equal name * 2, @site.config.description + assert_equal I18n.t('site_service.update', name: name), + @site.repository.rugged.head.target.message + end end diff --git a/test/models/site/config_test.rb b/test/models/site/config_test.rb index 4ccaf37..1ecbaba 100644 --- a/test/models/site/config_test.rb +++ b/test/models/site/config_test.rb @@ -23,15 +23,12 @@ class ConfigText < ActiveSupport::TestCase @site.config.lang = 'es' end - assert @site.config.write(@usuarie) + assert @site.config.write config = Site::Config.new(@site) assert_equal 'Test', config.name assert_equal 'es', config.lang - - assert_equal I18n.t('sites.repository.config'), - @site.repository.rugged.head.target.message end test 'se puede obtener información' do