Skip to content

Commit

Permalink
Merge pull request #134 from activebridge/feature/add_posts
Browse files Browse the repository at this point in the history
Feature/add posts
  • Loading branch information
katatsu12 authored Mar 26, 2024
2 parents 06d94de + 786a950 commit 3eb763d
Show file tree
Hide file tree
Showing 189 changed files with 8,585 additions and 329 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ To add a new post to your blog:
time-to-read: 2 min
scripts: [post]
popular: true
hidden: true
---
Content
Expand All @@ -56,6 +57,7 @@ To add a new post to your blog:
* `layout`: It's crucial to use the exact layout specified for blog posts. Always use `layout: post` for blog posts to ensure your post is displayed correctly within the site's theme.
* `scripts`: By default, use `[post]`. If your post requires additional specific JavaScript files, list them here without the `.js` extension. These files should be located in the `assets/js` directory. For example, to include a script named `example.js`, add it to the array like so: `scripts: [post, example]`. Follow the format shown in the example for proper script loading.
* `popular`: An optional boolean value. If set to true, the post will be added to a "Popular Post" section on the website. If not needed, this setting can be omitted.
* `hidden`: An optional boolean value. If set to true, the post will not show. If not needed, this setting can be omitted.


### SEO
Expand Down Expand Up @@ -83,7 +85,7 @@ To add a new post to your blog:
popular: true
# Only for SEO
author-url: "https://www.linkedin.com/in/autor"
author-url: "https://www.linkedin.com/in/author"
date-modified: "2024-01-10"
article-body: This is the some main elements of the blog post
---
Expand Down
2 changes: 1 addition & 1 deletion _includes/post_prev_next_block.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="prev-next-posts-mob-title"><p>Read Also</p></div>
<div class="posts prev-next-posts">
{% assign current_index = -1 %}
{% assign posts_in_category = site.posts | where: 'categories', page.category %}
{% assign posts_in_category = site.posts | where: "categories", page.category | where_exp: "item", "item.hidden != true"%}
{% assign number_of_posts = posts_in_category | size %}

{% for post in posts_in_category %}
Expand Down
172 changes: 172 additions & 0 deletions _posts/2016-01-27-fighting-duplication-in-angularjs-controllers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
---
author: Eugene Korpan
author-position:
background: fighting-duplication-in-angularjs-controllers-back
category: engineering
date: "2016-01-27"
layout: post
post-id: fighting-duplication-in-angularjs-controllers
post-title: "Fighting duplication in AngularJS controllers"
time-to-read: 4 min
scripts: [post]
hidden: true

author-url: ""
article-body: ""
date-modified: "2020-02-25"
description: "In this article I would like to discuss code duplication in AngularJS controllers"
title: "How to fight with code duplication in Angular controllers"
---

There are not so many challenges you face when working with AngularJS framework. In this article I would like to discuss code duplication in AngularJS controllers. Duplication is not something new but with AngularJS I saw some approaches that I didn’t like but those are widely used.

So let’s say we have a simple forum app. The home page displays the list of all posts and user is able to like or dislike particular post. In AngularJS controller we would have someting like: below I’m going to use coffeescript.

```coffeescript

forumApp.controller 'PostsController', [
'$scope', 'Post'
($scope, Post) ->

$scope.posts = Post.query()

markAsVoted = (post, vote) ->
Post.update
id: post.id
vote: vote
, (response) ->
post.votes = response.votes

$scope.voteUp = (post) ->
markAsVoted(post, true)

$scope.voteDown = (post) ->
markAsVoted(post, false)

]
```
So far nothing special. We have two functions that get executed once user liked or disliked a post.

Let’s move further.

Then we need to implement a post page. When user found some post interesting he clicks on it and he is able to read the rest of the post and see other users’ comments. So we provide new AngularJS controller with appropriate template.

```coffeescript

forumApp.controller 'PostDetailsController', [
'$scope', 'Post', '$routeParams'
($scope, Post, $routeParams) ->

$scope.post = Post.get id: $routeParams.id
]
```
Then we think that user should be able to like/dislike this post within this page as well. On the template layer we would probably use ng-include and that’s it. What about AngularJS controller? First and easiest solution is simply copy and paste appropriate functions from PostsController.

```coffeescript

forumApp.controller 'PostDetailsController', [
'$scope', 'Post', '$routeParams'
($scope, Post, $routeParams) ->

$scope.post = Post.get id: $routeParams.id

markAsVoted = (post, vote) ->
Post.update
id: post.id
vote: vote
, (response) ->
post.votes = response.votes

$scope.voteUp = (post) ->
markAsVoted(post, true)

$scope.voteDown = (post) ->
markAsVoted(post, false)

]
```
And as we can see we’ve got the duplication.

For some reason I found that many AngularJS users use $rootScope to solve this problem. If we go this way then our controllers would look like:

```coffeescript

forumApp.controller 'PostsController', [
'$scope', 'Post', '$rootScope'
($scope, Post, $rootScope) ->

$rootScope.postController = $scope

$scope.posts = Post.query()

markAsVoted = (post, vote) ->
Post.update
id: post.id
vote: vote
, (response) ->
post.votes = response.votes

$scope.voteUp = (post) ->
markAsVoted(post, true)

$scope.voteDown = (post) ->
markAsVoted(post, false)

]
forumApp.controller 'PostDetailsController', [
'$scope', 'Post', '$routeParams', '$rootScope'
($scope, Post, $routeParams, $rootScope) ->

$scope.post = Post.get id: $routeParams.id

$scope.voteUp = (post) ->
$rootScope.postController.voteUp(post)

$scope.voteDown = (post) ->
$rootScope.postController.voteDown(post)
]
```
At first glance seems like delegating our functions to postsController solves the problem: no duplication, minimum updates in existing controllers. But I think this is totally wrong! In my ideal world developers just don’t use $rootScope. If we continue using this approach then the whole AngularJS app would turn to spaghetti, all controllers call functions in each other ruining single responsibility principle and hence would be hard to understand what is going on here, debug and maintain.

I’m pretty sure there are many other better solutions to this problem and I would like to describe one of them. The idea is taken from ruby mixins and applying Decorator pattern. So we just extract duplicated code into a decorator and apply it for $scope of each controller we need.

Here is how a decorator would look like:

```coffeescript

forumApp.factory 'postDecorator', [
'Post'
(Post) ->
markAsVoted = (post, vote) ->
Post.update
id: post.id
vote: vote
, (response) ->
post.votes = response.votes

$scope.voteUp = (post) ->
markAsVoted(post, true)

$scope.voteDown = (post) ->
markAsVoted(post, false)
]
And then our controllers:

forumApp.controller 'PostsController', [
'$scope', 'Post', 'postDecorator'
($scope, Post, postDecorator) ->

postDecorator($scope)

$scope.posts = Post.query()
]
forumApp.controller 'PostDetailsController', [
'$scope', 'Post', '$routeParams', 'postDecorator'
($scope, Post, $routeParams, postDecorator) ->

postDecorator($scope)

$scope.post = Post.get id: $routeParams.id
]
```
Looks much better, doesn’t it?
66 changes: 66 additions & 0 deletions _posts/2016-02-02-five-ways-to-keep-your-code-cleen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
author: Alex Galushka
author-position: "CEO / Tech lead and backend developer"
background: five-ways-to-keep-your-code-cleen-back
category: engineering
date: "2016-02-02"
layout: post
post-id: five-ways-to-keep-your-code-cleen
post-title: "5 ways to keep your code clean"
time-to-read: 2 min
scripts: [post]
hidden: true

author-url: ""
article-body: ""
date-modified: "2020-02-25"
description: "Rails community's invented many gems what are constantly analyzing your code and they give you to know about
stuff what you should improve"
title: "How to Keep Your Code Clean"
---

Do you think your code clean, useful and readable? I’m sure you're wrong!
Rails community's invented many gems what are constantly analyzing your code and they give you to know about stuff what you should improve.
Let me introduce some of these gems:

`rails_best_practices`

> ’sudo gem install rails_best_practices’
and run rails_best_practices .By the way, there are many configurations, so you should read documentation. Best Practices experience can be found on site


`rubocop`

> sudo gem install rubocop
So you can run rubocop
I have found a lot of tweaks in documentation

`Flay` from Ruby Sadist

> just install sudo gem install flay
and run in command line flay app/models/*.rb

Have you heard about Bullet gem?
Improve your SQL queries with Bullet
The Bullet gem is designed to help you increase your application’s performance by reducing the number of queries it makes. It will watch your queries while you develop your application and notify you when you should add eager loading (N+1 queries). When you’re using eager loading that isn’t necessary and when you should use a counter cache.
add it into a Gemfile

> gem 'bullet', group: 'development'
Append to config/environments/development.rb initializer with the following code:

> config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.bullet_logger = true Bullet.console = true Bullet.growl = trueend
More information you can find on official GitHub page.
What benefits are from Brakeman gem? Just install gem brakeman and run from app directory brakeman. So, after analyzing it provide information in next categories:

SUMMARY
SECURITY WARNINGS
Controller Warnings
Model Warnings

P.S.
You have to know Ruby Style Guide, Rails Style Guide and Better Specs or Rspec Best Practices. And gems described above should just help you fix missed!
Have clean code!
52 changes: 52 additions & 0 deletions _posts/2016-02-02-simple-way-to-create-public-pages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
author: Alex Galushka
author-position: "CEO / Tech lead and backend developer"
background: simple-way-to-create-public-pages-back
category: engineering
date: "2016-02-02"
layout: post
post-id: simple-way-to-create-public-pages
post-title: "Simple way to create public pages"
time-to-read: 2 min
scripts: [post]
hidden: true

author-url: ""
article-body: ""
date-modified: "2020-02-25"
description: "So, it's generally understood, they are used when there is no need for dynamic info or pulling from the
database"
title: "How to Create Public Pages. Ruby"
---

Have you ever heard about public pages in Ruby? And what does it mean? So, it's generally understood, they are used when there is no need for dynamic info or pulling from the database.
As usual Ruby on Rails developers implement some pre-built gems. But I would like to show you another way to create public pages in your rails apps such as About and FAQ. By this means, if your public page doesn't have anything similar in your app layout you can put simple HTML page to your public folder. There is such way:

```ruby
class PagesController < ApplicationController
def about
end

def faq
end
end
```
However, I guess this kind of implementation is very complicated because we need to add many actions and routes to achieve the result. Nevertheless, one should not forget that the main REST principle says that everything is a resource. So let's look at this pages as resources.
`routes.rb:
get ':page', to: 'pages#show', as: :page
pages_controller.rb:`

```ruby
class PagesController < ApplicationController
def show
render params[:page]
end
end
```
Then, you should use path helper to get these pages:
`page_path(:about), page_path(:faq)`
But there is the possibility that user can pass the invalid value as page parameter and template missing exception resides with status 500 that is not good. To avoid that we will add constraints to our route.
`routes.rb:
get ':page', to: 'pages#show', as: :page, constraints: { page: /(howitworks|careers|about|faq)/ }`
If you want to add a new public page you just need to create a view for it in ‘views/pages’ folder and add the constraint to routes.
I think, this method will be ease to implement and achieve good results for building an app with public pages.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
author: Alex Galushka
author-position: "CEO / Tech lead and backend developer"
background: simple-way-to-create-public-pages-back
category: engineering
date: "2016-02-02"
layout: post
post-id: unobtrusive-scripting-adapter-vs-remote-part-ajax-file-uploader
post-title: "Unobtrusive scripting adapter vs Remotipart"
time-to-read: 2 min
scripts: [post]
hidden: true

author-url: ""
article-body: ""
date-modified: "2020-02-25"
description: "Many of web applications require an easy and common way to upload different type data"
title: "A definitive guide to Rails’ unobtrusive JavaScript adapter"
---

In this short instruction, I would like to share how to use AJAX file upload in Ruby on Rails. Many of web applications require an easy and common way to upload different type data. To solve this issue, Rails has nice Unobtrusive scripting adapter. It takes care of remote forms and links to your application. But in fact, I actually think, there is one point it can not handle: the AJAX file upload. It is the provide the routes.rb:

> get ':page', to: 'pages#show', as: :page
pages_controller.rb:

```ruby
class PagesController<Application Controller
def show
render params[:page]
end
end
```

ability to handle that to other libs by adding the custom event called`ajax: aborted: file`.


I've made some experiments with deep insight of this method. I’ve found there is only one lib uses this event and provides the ability to upload files with remote forms. It's named the remote part. The remote part uses the iframe workaround. But there should be the other way to do it.
Firstly, I am thinking about the formData interface but it is not supported in by Internet Explorer. So there is only one alternative left - base64.

Let’s code some coffee:

```javascript
$(document).on 'ajax:aborted:file', 'form', (e, inputs) ->
j = 1
form = $(@
$.map inputs, (input, i) ->
fr = new FileReader()
fr.readAsDataURL(input.files[0])
fr.onload = ->
form.append("<input type='hidden' name='#{input.name}' value='#{fr.result}' />")
$.rails.handleRemote(form) if (inputs.length == j)
j++
return false
```
In such a way the files will be sending as base64 strings under same names. Next step is decoding the files on the server. We were using the carrier wave for file uploading and I've found the carrierwave-base64 gem that is doing exactly what I need.
Just add it to your Gemfile, and mount the uploader mount_base64_uploader :image, ImageUploader.
Also, it is one more additional step is to add the parameter filter to keep your logs clean.
> config.filter_parameters += [:image]
All browser is supported except IE9 and lower. In case IE9, the form will be submitted to regular HTML type.
I have extracted the javascript to separate rails gem. Also, it would be nice to have the rack middleware to encode the base64 string on middleware level.
Loading

0 comments on commit 3eb763d

Please sign in to comment.