From cad881db1c2a17227db655b4590117ba930e8858 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 17 Sep 2019 18:27:51 -0300 Subject: [PATCH] crear licencias y gestionar correctamente las colecciones por idioma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit habia un monton de inconsistencias que hacian que los posts se guardaran en cualquier lado, que empezaron a saltar apenas introdujimos la gestión de licencias --- app/models/metadata_lang.rb | 16 +++++++ app/models/metadata_path.rb | 2 +- app/models/post.rb | 15 ++++-- app/models/post_relation.rb | 24 +++++++--- app/services/post_service.rb | 17 +++---- app/services/site_service.rb | 24 ++++++++++ app/views/posts/attribute_ro/_lang.haml | 3 ++ app/views/posts/attributes/_lang.haml | 2 + config/locales/en.yml | 1 + config/locales/es.yml | 1 + db/seeds.rb | 4 +- db/seeds/licencias.yml | 48 ++----------------- .../api/v1/sites_controller_test.rb | 2 +- test/controllers/posts_controller_test.rb | 2 +- test/controllers/sites_controller_test.rb | 15 ++++-- test/models/post_test.rb | 2 + test/models/site_test.rb | 6 +-- test/test_helper.rb | 2 + 18 files changed, 110 insertions(+), 76 deletions(-) create mode 100644 app/models/metadata_lang.rb create mode 100644 app/views/posts/attribute_ro/_lang.haml create mode 100644 app/views/posts/attributes/_lang.haml diff --git a/app/models/metadata_lang.rb b/app/models/metadata_lang.rb new file mode 100644 index 0000000..1ad381a --- /dev/null +++ b/app/models/metadata_lang.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# Un campo de idioma +class MetadataLang < MetadataTemplate + def default_value + I18n.locale + end + + def value + self[:value] || document.collection.label.to_sym + end + + def values + I18n.available_locales + end +end diff --git a/app/models/metadata_path.rb b/app/models/metadata_path.rb index b3f3b1a..05e3ed4 100644 --- a/app/models/metadata_path.rb +++ b/app/models/metadata_path.rb @@ -28,7 +28,7 @@ class MetadataPath < MetadataTemplate end def lang - post.lang + post.lang.value end def slug diff --git a/app/models/post.rb b/app/models/post.rb index ace2487..7aab812 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -12,8 +12,8 @@ class Post < OpenStruct # Atributos por defecto DEFAULT_ATTRIBUTES = %i[site document layout].freeze # Otros atributos que no vienen en los metadatos - PRIVATE_ATTRIBUTES = %i[lang path slug attributes errors].freeze - PUBLIC_ATTRIBUTES = %i[date].freeze + PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze + PUBLIC_ATTRIBUTES = %i[lang date].freeze # Redefinir el inicializador de OpenStruct # @@ -47,6 +47,7 @@ class Post < OpenStruct required: template['required']) end + load_lang! load_slug! load_date! load_path! @@ -142,7 +143,7 @@ class Post < OpenStruct FileUtils.rm_f path.absolute # TODO: Devolver self en lugar de todo el array - site.posts(lang: lang).reject! do |post| + site.posts(lang: lang.value).reject! do |post| post.path.absolute == path.absolute end end @@ -231,6 +232,7 @@ class Post < OpenStruct save end + alias update update_attributes private @@ -292,6 +294,13 @@ class Post < OpenStruct required: true) end + def load_lang! + self.lang = MetadataLang.new(document: document, site: site, + layout: layout, name: :lang, + type: :lang, post: self, + required: true) + end + # Ejecuta la acción de guardado en cada atributo def save_attributes! attributes.map do |attr| diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index 4fa206f..9e0df2e 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -15,8 +15,8 @@ class PostRelation < Array # Genera un artículo nuevo con los parámetros que le pasemos y lo suma # al array def build(**args) - args[:document] ||= build_document args[:lang] ||= I18n.locale + args[:document] ||= build_document(collection: args[:lang]) args[:layout] = build_layout(args[:layout]) post = Post.new(site: site, **args) @@ -31,12 +31,22 @@ class PostRelation < Array post end + alias find_generic find + def find(id) - super() do |p| + find_generic do |p| p.id == id end end + # Encuentra el primer post por el valor de un atributo + # XXX: Acepta cualquier atributo + def find_by(**args) + find_generic do |p| + p.public_send(args.first.first).try(:value) == args.first.last + end + end + # Intenta guardar todos y devuelve true si pudo def save_all map(&:save).all? @@ -51,14 +61,14 @@ class PostRelation < Array end # Devuelve una colección Jekyll que hace pasar el documento - def build_collection - Jekyll::Collection.new(site.jekyll, 'posts') + def build_collection(label:) + Jekyll::Collection.new(site.jekyll, label.to_s) end # Un documento borrador con algunas propiedades por defecto - def build_document - doc = Jekyll::Document.new('', site: site.jekyll, - collection: build_collection) + def build_document(collection:) + col = build_collection(label: collection) + doc = Jekyll::Document.new('', site: site.jekyll, collection: col) doc.data['date'] = Date.today.to_time doc end diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 9c1566f..0ef151b 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -8,11 +8,9 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do # @return Post def create # TODO: Implementar layout - self.post = site.posts.build - # TODO: No podemos pasar los post_params a build aun porque para - # saber los parámetros tenemos que haber instanciado el post - # primero. - post.update_attributes(post_params) && commit(action: :created) + self.post = site.posts(lang: params[:post][:lang] || I18n.locale).build + + commit(action: :created) if post.update(post_params) # Devolver el post aunque no se haya salvado para poder rescatar los # errores @@ -20,10 +18,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end def update - # TODO: No podemos pasar los post_params a build aun porque para - # saber los parámetros tenemos que haber instanciado el post - # primero. - post.update_attributes(post_params) && commit(action: :updated) + commit(action: :updated) if post.update(post_params) # Devolver el post aunque no se haya salvado para poder rescatar los # errores @@ -31,7 +26,9 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do end def destroy - post.destroy! && commit(action: :destroyed) + post.destroy! + + commit(action: :destroyed) if post.destroyed? post end diff --git a/app/services/site_service.rb b/app/services/site_service.rb index 94bedbb..8d2a090 100644 --- a/app/services/site_service.rb +++ b/app/services/site_service.rb @@ -17,6 +17,8 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do commit_config(action: :create) end + add_licencias + site end @@ -46,5 +48,27 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do site.roles << Rol.new(site: site, usuarie: usuarie, temporal: temporal, rol: rol) end + + # Crea la licencia del sitio para cada locale disponible + # rubocop:disable Metrics/MethodLength + def add_licencias + I18n.available_locales.each do |locale| + Mobility.with_locale(locale) do + params = ActionController::Parameters.new( + post: { + lang: locale, + title: site.licencia.name, + author: %w[Sutty], + permalink: "#{I18n.t('activerecord.models.licencia').downcase}/", + content: site.licencia.deed + } + ) + + PostService.new(site: site, usuarie: usuarie, params: params) + .create + end + end + end + # rubocop:enable Metrics/MethodLength end # rubocop:enable Metrics/BlockLength diff --git a/app/views/posts/attribute_ro/_lang.haml b/app/views/posts/attribute_ro/_lang.haml new file mode 100644 index 0000000..cb88aa1 --- /dev/null +++ b/app/views/posts/attribute_ro/_lang.haml @@ -0,0 +1,3 @@ +%tr{ id: attribute } + %th= post_label_t(attribute, post: post) + %td= t(".#{metadata.value}") diff --git a/app/views/posts/attributes/_lang.haml b/app/views/posts/attributes/_lang.haml new file mode 100644 index 0000000..d0290ed --- /dev/null +++ b/app/views/posts/attributes/_lang.haml @@ -0,0 +1,2 @@ +-# TODO: Implementar i18n += hidden_field 'post[lang]', I18n.locale diff --git a/config/locales/en.yml b/config/locales/en.yml index 24a3c5d..47a0d7d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -59,6 +59,7 @@ en: activerecord: models: usuarie: User + licencia: License attributes: usuarie: email: 'E-mail address' diff --git a/config/locales/es.yml b/config/locales/es.yml index 72030ba..03d8cfc 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -61,6 +61,7 @@ es: activerecord: models: usuarie: Usuarie + licencia: Licencia attributes: usuarie: email: 'Correo electrónico' diff --git a/db/seeds.rb b/db/seeds.rb index c36c43b..fd30377 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,7 +5,7 @@ designs = YAML.safe_load(File.read('db/seeds/designs.yml')) designs.each do |d| design = Design.find_or_create_by(gem: d['gem']) - design.update_attributes d + design.update d end licencias = YAML.safe_load(File.read('db/seeds/licencias.yml')) @@ -13,7 +13,7 @@ licencias = YAML.safe_load(File.read('db/seeds/licencias.yml')) licencias.each do |l| licencia = Licencia.find_or_create_by(icons: l['icons']) - licencia.update_attributes l + licencia.update l end YAML.safe_load(File.read('db/seeds/sites.yml')).each do |site| diff --git a/db/seeds/licencias.yml b/db/seeds/licencias.yml index 27c2931..73bf5ab 100644 --- a/db/seeds/licencias.yml +++ b/db/seeds/licencias.yml @@ -7,14 +7,6 @@ description_en: 'The Peer Production License is a license that allows use for any purpose under the same terms, except for commercial purposes, which are only allowed for collectives, cooperatives and other worker-owned "enterprises". We recommend this license if you are inclined towards non-profit terms, since it allows the development of more ethical economies while fending off capitalistic for-profit uses. If you want to know more about it, we invite you to read [The Telekommunist Manifesto](http://networkcultures.org/blog/publication/no-03-the-telekommunist-manifesto-dmytri-kleiner/)' description_es: 'La licencia de Producción de Pares permite el uso con cualquier propósito bajo la misma licencia, a excepción de los usos comerciales que solo están permitidos a colectivos, cooperativas y otras "empresas" en manos de sus trabajadorxs. Recomendamos esta licencia si te estabas inclinando hacia términos sin fines de lucro, ya que permite el desarrollo de economías más éticas al mismo tiempo que impide la explotación capitalista. Si te interesa saber más, te invitamos a leer [El manifiesto telecomunista](https://endefensadelsl.org/manifiesto_telecomunista.html).' deed_en: | - --- - title: License - permalink: license/ - layout: post - --- - - # Peer Production License (human-readable version) - This is a human-readable summary of the [full license](https://wiki.p2pfoundation.net/Peer_Production_License). @@ -54,14 +46,6 @@ such as publicity, privacy, or moral rights may limit how you use the material. deed_es: | - --- - title: Licencia - permalink: licencia/ - layout: post - --- - - # Licencia de producción de pares (versión legible por humanas) - Esto es un resumen legible por humanas del [texto legal (la licencia completa)](http://endefensadelsl.org/ppl_es.html) @@ -113,7 +97,7 @@ hacerlo es enlazar a esta página. - icons: "/images/by.png" - name_en: 'Attribution 4.0 International (CC BY 4.0)' + name_en: 'Creative Commons Attribution 4.0 International (CC BY 4.0)' description_en: "This license gives everyone the freedom to use, adapt, and redistribute the contents of your site by requiring attribution only. We recommend this license if you're publishing @@ -121,7 +105,7 @@ you want to be attributed. Users of the site will have to mention the source and indicate if they made changes to it." url_en: 'https://creativecommons.org/licenses/by/4.0/' - name_es: 'Atribución 4.0 Internacional (CC BY 4.0)' + name_es: 'Creative Commons Atribución 4.0 Internacional (CC BY 4.0)' description_es: "Esta licencia permite a todes la libertad de usar, adaptar/modificar y redistribuir los contenidos de tu sitio con la única condición de atribuirte la autoría original. Recomendamos esta @@ -131,12 +115,6 @@ indicar si hicieron cambios." url_es: 'https://creativecommons.org/licenses/by/4.0/deed.es' deed_en: | - --- - title: License - permalink: license/ - layout: post - --- - This is a human-readable summary of (and not a substitute for) the [license](https://creativecommons.org/licenses/by/4.0/legalcode). @@ -173,12 +151,6 @@ such as publicity, privacy, or moral rights may limit how you use the material. deed_es: | - --- - title: Licencia - permalink: licencia/ - layout: post - --- - Este es un resumen legible por humanes (y no un sustituto) de la [licencia](https://creativecommons.org/licenses/by/4.0/legalcode). @@ -215,8 +187,8 @@ publicidad, privacidad, o derechos morales pueden limitar la forma en que utilice el material. - icons: "/images/sa.png" - name_en: "Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)" - name_es: "Atribución-CompartirIgual 4.0 Internacional (CC BY-SA 4.0)" + name_en: "Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)" + name_es: "Creative Commons Atribución-CompartirIgual 4.0 Internacional (CC BY-SA 4.0)" url_en: 'https://creativecommons.org/licenses/by-sa/4.0/' url_es: 'https://creativecommons.org/licenses/by-sa/4.0/deed.es' description_en: "This license is the same as the CC-BY 4.0 but it adds @@ -232,12 +204,6 @@ están permitidos, las mejoras hechas con fines de lucro deben ser compartidas bajo la misma licencia." deed_en: | - --- - title: License - permalink: license/ - layout: post - --- - This is a human-readable summary of (and not a substitute for) the [license](https://creativecommons.org/licenses/by-sa/4.0/legalcode). @@ -279,12 +245,6 @@ such as publicity, privacy, or moral rights may limit how you use the material. deed_es: | - --- - title: Licencia - permalink: licencia/ - layout: post - --- - Este es un resumen legible por humanes (y no un sustituto) de la [licencia](https://creativecommons.org/licenses/by/4.0/legalcode). diff --git a/test/controllers/api/v1/sites_controller_test.rb b/test/controllers/api/v1/sites_controller_test.rb index 044ab45..520c0b9 100644 --- a/test/controllers/api/v1/sites_controller_test.rb +++ b/test/controllers/api/v1/sites_controller_test.rb @@ -33,7 +33,7 @@ module Api test 'se puede obtener un listado de todos' do get v1_sites_url, headers: @authorization, as: :json - assert_equal [@site.name], JSON.parse(response.body) + assert_equal Site.all.pluck(:name), JSON.parse(response.body) end end end diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb index 06fb85e..10a2db1 100644 --- a/test/controllers/posts_controller_test.rb +++ b/test/controllers/posts_controller_test.rb @@ -39,7 +39,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest # TODO: implementar reload? site = Site.find(@site.id) - new_post = site.posts.first + new_post = site.posts.find_by(title: title) assert_equal 302, response.status diff --git a/test/controllers/sites_controller_test.rb b/test/controllers/sites_controller_test.rb index 19b9885..a8bb31d 100644 --- a/test/controllers/sites_controller_test.rb +++ b/test/controllers/sites_controller_test.rb @@ -32,7 +32,7 @@ class SitesControllerTest < ActionDispatch::IntegrationTest test 'se pueden crear' do name = SecureRandom.hex - design = create :design + design = Design.all.sample post sites_url, headers: @authorization, params: { site: { @@ -40,7 +40,7 @@ class SitesControllerTest < ActionDispatch::IntegrationTest title: name, description: name * 2, design_id: design.id, - licencia_id: create(:licencia).id, + licencia_id: Licencia.first.id, deploys_attributes: { '0' => { type: 'DeployLocal' @@ -62,8 +62,15 @@ class SitesControllerTest < ActionDispatch::IntegrationTest 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 + + # TODO: Esto requiere que podamos buscar hacia atrás en la historia + # del repositorio + # assert_equal I18n.t('site_service.create', name: name), + # site.repository.rugged.head.target.message + + I18n.available_locales.each do |locale| + assert_equal 3, site.posts(lang: locale).size + end site.destroy end diff --git a/test/models/post_test.rb b/test/models/post_test.rb index b52f5d9..996a026 100644 --- a/test/models/post_test.rb +++ b/test/models/post_test.rb @@ -42,6 +42,8 @@ class PostTest < ActiveSupport::TestCase if metadata.empty? assert_not @post.document.data[attr.to_s].present? + elsif metadata.type == :lang + assert @post.path.relative.starts_with?("_#{metadata.value}/") elsif @post.document.respond_to? attr assert_equal metadata.value, @post.document.send(attr), attr else diff --git a/test/models/site_test.rb b/test/models/site_test.rb index 817613d..78e3f57 100644 --- a/test/models/site_test.rb +++ b/test/models/site_test.rb @@ -60,7 +60,7 @@ class SiteTest < ActiveSupport::TestCase end test 'se puede leer un sitio' do - site = create :site, name: 'sutty.nl' + site = create :site assert site.valid? assert !site.posts.empty? @@ -87,10 +87,10 @@ class SiteTest < ActiveSupport::TestCase end test 'el sitio tiene artículos en distintos idiomas' do - site = create :site, name: 'sutty.nl' + @site = create :site I18n.available_locales.each do |locale| - assert site.posts(lang: locale).size.positive? + assert @site.posts(lang: locale).size.positive? end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index cf13995..c9fda06 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -5,6 +5,8 @@ require 'rails/test_help' require 'open3' require 'sucker_punch/testing/inline' +Rails.application.load_seed + # rubocop:disable Style/ClassAndModuleChildren class ActiveSupport::TestCase include FactoryBot::Syntax::Methods