BlogAm is a multilingual blog platform built on Hanami 2.3.2, featuring a Liquid-based theming system and Elasticsearch search. The project is designed to be compatible with Ruby on Rails, enabling easy data export/import between applications.
- 🇬🇧 English (en)
- 🇵🇱 Polski (pl)
- 🇺🇦 Українська (ua) - default
- 🇷🇺 Русский (ru)
- Ruby 3.2+
- PostgreSQL 14+
- Elasticsearch 8.x
- Node.js 18+ (for asset pipeline)
# Clone the repository
git clone https://github.com/webgate-pro/blog_am.git
cd blog_am
# Install Ruby dependencies
bundle install
# Install Node.js dependencies
npm install
# Copy and customize configuration
cp config/settings/development.yml.example config/settings/development.yml
# Create database and run migrations
rake db:setup
# Start development server
rake serverThe project uses the config gem instead of .env environment variables:
# config/settings/development.yml
database:
url: postgres://localhost/blog_am_development
session:
secret: your-secret-key-here-at-least-64-characters
elasticsearch:
host: http://localhost:9200
index_prefix: blog_amblog_am/
├── app/
│ ├── actions/ # HTTP actions (controllers)
│ ├── relations/ # Table definitions (ROM)
│ ├── repos/ # Repositories (data access)
│ ├── structs/ # Data structures (entities)
│ ├── services/ # Business services
│ │ ├── theme_renderer.rb # Liquid renderer
│ │ └── elasticsearch_service.rb
│ └── views/ # Hanami views
├── config/
│ ├── db/
│ │ ├── migrate/ # Database migrations
│ │ └── seeds/ # Seed data (modular)
│ │ ├── 000_users.rb
│ │ ├── 010_categories.rb
│ │ ├── 020_tags.rb
│ │ ├── 030_posts.rb
│ │ └── data/posts.yml # Content in YAML
│ ├── locales/ # I18n translations
│ ├── settings/ # Per-environment configuration
│ └── routes.rb # Routing
├── lib/tasks/ # Rake tasks
│ ├── db.rake # Database management
│ ├── search.rake # Elasticsearch
│ ├── server.rake # Development server
│ └── quality.rake # RuboCop, SimpleCov
├── themes/ # Liquid themes
│ └── default/
│ ├── layouts/
│ ├── partials/
│ ├── assets/
│ └── theme.yml
├── spec/ # RSpec tests
│ ├── factories/ # FactoryBot
│ ├── repos/ # Repository tests
│ ├── services/ # Service tests
│ ├── requests/ # HTTP tests
│ ├── features/ # Integration tests
│ └── structs/ # Struct tests
└── docs/ # Documentation
rake db:create # Create database
rake db:drop # Drop database
rake db:migrate # Run migrations
rake db:seed # Load seed data
rake db:setup # create + migrate + seed
rake db:reset # drop + setup
rake db:recreate # reset with confirmation
rake db:status # Migration statusrake server # Start Puma on port 2300
rake console # Hanami consolerake search:status # Check ES status
rake search:setup # Create index
rake search:reindex # Reindex all content
rake search:rebuild # drop + setup + reindex
rake search:test[query] # Test searchrake rubocop # Check code style
rake rubocop_fix # Auto-fix issues
rake quality # RSpec + RuboCop
rake quality:coverage # RSpec with SimpleCov| Table | Description |
|---|---|
users |
Users and administrators |
posts |
Blog articles/posts |
videos |
Video content |
photos |
Photo galleries |
tags |
Tags |
taggings |
Polymorphic many-to-many for tags |
categories |
Categories (hierarchical) |
comments |
Comments (polymorphic) |
pages |
Static pages (About, Terms, etc.) |
themes |
Installed themes |
site_settings |
Site settings (JSONB) |
partners |
Blog partners/friends |
media_attachments |
Media attachments |
- UUID as primary keys (protection against enumeration attacks)
- JSONB for multilingual content (
title,content, etc.) - Full Rails compatibility (naming conventions)
- Polymorphic associations (tags, comments, attachments)
BlogAm uses Elasticsearch for full-text search across multiple languages:
# Configuration in config/settings.yml
elasticsearch:
host: http://localhost:9200
index_prefix: blog_am
log: false
# Reindex after changes
rake search:reindexSearch includes:
- Titles, subtitles, leads, content
- All language versions
- Fuzzy matching (typo tolerance)
- Result highlighting
BlogAm uses Liquid as its template engine:
themes/my-theme/
├── theme.yml # Theme metadata
├── layouts/
│ └── application.liquid
├── partials/
├── index.liquid # Homepage
├── post.liquid # Single article
├── posts/index.liquid # Article listing
├── videos/
├── photos/
├── search/
└── assets/
├── css/
├── js/
└── img/
{{ 'navigation.home' | t }} {# I18n translation #}
{{ post.published_at | date_format }} {# Date formatting #}
{{ post.content | strip_html | truncate_words: 50 }}
{{ post | post_url }} {# Post URL #}
{{ content | reading_time }} min {# Reading time #}Detailed documentation: docs/THEMES.md
# Multilingual field structure in database
{
"en" => "Title in English",
"ua" => "Заголовок українською",
"ru" => "Заголовок на русском",
"pl" => "Tytuł po polsku"
}# config/locales/ua.yml
ua:
navigation:
home: "Головна"
about: "Про мене"{{ 'navigation.home' | t }}GET /locale/en # Switch to English
GET /locale/ua # Switch to Ukrainian
# Run all tests
bundle exec rspec
# Run specific file
bundle exec rspec spec/repos/post_repo_spec.rb
# With code coverage (SimpleCov)
bundle exec rspec
open coverage/index.html
# Fast tests only (skip features)
bundle exec rspec --tag ~type:featurespec/repos/- Repository tests (CRUD, queries)spec/services/- Service tests (ThemeRenderer, Elasticsearch)spec/requests/- HTTP tests (actions, routing)spec/features/- Integration tests (browser)spec/structs/- Data structure tests
Current: ~82% Line Coverage, ~33% Branch Coverage
- UUID instead of incremental IDs (protection against enumeration)
- BCrypt for passwords
- Liquid - safe template language (no Ruby code execution)
- CSRF protection in forms
- Sanitization of user content
- Content Security Policy headers
The database is designed according to Rails conventions:
# In a Rails application
class Post < ApplicationRecord
belongs_to :author, class_name: "User"
belongs_to :category
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
has_many :comments, as: :commentable
# Multilingual with mobility gem
extend Mobility
translates :title, :subtitle, :lead, :content, backend: :jsonb
end- Stage 1: Data structure and migrations (UUID, Rails-compatible)
- Stage 2: Multilingual seeds EN/UA/RU/PL
- Stage 3: HTML/CSS/JS layout adaptation
- Stage 4: Liquid theme system + documentation
- Stage 5: RSpec tests (82% coverage)
- Elasticsearch search engine
- RuboCop + SimpleCov
- Stage 6: Administrative panel
Author: Jerzy Sładkowski
Company: Webgate Systems LTD
Blog: Ayder Muzhdabaiev
License: GNU Affero General Public License v3.0
This project is part of a larger media platform.