Skip to content

Commit

Permalink
#63 badges
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Feb 2, 2020
1 parent 80678c1 commit b1b5f30
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 11 deletions.
10 changes: 8 additions & 2 deletions front/front_project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,14 @@
flash(iri.cut('/p').append(project.id), "The review ##{review.id} in the project ##{project.id} removed")
end

post '/do-review' do
project = the_author.projects.get(params[:project].to_i)
post '/p/{id}/post' do
project = the_author.projects.get(params[:id].to_i)
review = project.reviews.post(params[:text].strip)
flash(iri.cut('/p').append(project.id), "A new review ##{review.id} has been posted to the project ##{project.id}")
end

post '/p/{id}/attach' do
project = the_author.projects.get(params[:id].to_i)
badge = project.badges.attach(params[:text].strip)
flash(iri.cut('/p').append(project.id), "A new badge ##{badge.id} has been attached to the project ##{project.id}")
end
31 changes: 31 additions & 0 deletions liquibase/2020/004-unique-badge.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0"?>
<!--
(The MIT License)
Copyright (c) 2020 Yegor Bugayenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the 'Software'), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd" logicalFilePath="001-initial-schema.xml">
<changeSet id="004" author="yegor256">
<sql>
ALTER TABLE badge ADD CONSTRAINT unique_badge UNIQUE(project, text);
</sql>
</changeSet>
</databaseChangeLog>
43 changes: 43 additions & 0 deletions objects/badge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

# Copyright (c) 2020 Yegor Bugayenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require_relative 'xia'

# Badges.
# Author:: Yegor Bugayenko (yegor256@gmail.com)
# Copyright:: Copyright (c) 2020 Yegor Bugayenko
# License:: MIT
class Xia::Badge
attr_reader :id

def initialize(pgsql, project, id)
@pgsql = pgsql
@project = project
@id = id
end

def detach
raise Xia::Urror, 'Not enough karma to detach a badge' unless @project.author.karma.positive?
raise Xia::Urror, 'Can\'t delete the last badge' if @project.badges.all.count < 2
@pgsql.exec('DELETE FROM badge WHERE id=$1', [@id])
end
end
53 changes: 53 additions & 0 deletions objects/badges.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

# Copyright (c) 2020 Yegor Bugayenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require_relative 'xia'
require_relative 'badge'

# Badges.
# Author:: Yegor Bugayenko (yegor256@gmail.com)
# Copyright:: Copyright (c) 2020 Yegor Bugayenko
# License:: MIT
class Xia::Badges
def initialize(pgsql, project)
@pgsql = pgsql
@project = project
end

def get(id)
Xia::Badge.new(@pgsql, @project, id)
end

def all
@pgsql.exec('SELECT text FROM badge WHERE project=$1', [@project.id]).map { |r| r['text'] }
end

def attach(text)
raise Xia::Urror, 'Not enough karma to attach a badge' unless @project.author.karma.positive?
raise Xia::Urror, "The badge #{text.inspect} looks wrong" unless /^[a-z0-9]+$/.match?(text)
id = @pgsql.exec(
'INSERT INTO badge (project, text) VALUES ($1, $2) RETURNING id',
[@project.id, text]
)[0]['id'].to_i
get(id)
end
end
5 changes: 5 additions & 0 deletions objects/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

require_relative 'xia'
require_relative 'reviews'
require_relative 'badges'

# Project.
# Author:: Yegor Bugayenko (yegor256@gmail.com)
Expand All @@ -45,6 +46,10 @@ def reviews
Xia::Reviews.new(@pgsql, self)
end

def badges
Xia::Badges.new(@pgsql, self)
end

def delete
raise Xia::Urror, 'Not enough karma to delete a project' if @author.karma < 500
@pgsql.exec(
Expand Down
16 changes: 10 additions & 6 deletions objects/projects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,27 @@ def submit(platform, coordinates)
'INSERT INTO project (platform, coordinates, author) VALUES ($1, $2, $3) RETURNING id',
[platform, coordinates, @author.id]
)[0]['id'].to_i
get(id)
project = get(id)
project.badges.attach('newbie')
project
end

def recent(limit: 10)
q = [
'SELECT project.*, author.login',
'FROM project',
'JOIN author ON author.id=project.author',
'WHERE project.deleted IS NULL',
'ORDER BY project.created DESC',
'SELECT p.*, author.login,',
'ARRAY(SELECT text FROM badge WHERE project=p.id) as badges',
'FROM project AS p',
'JOIN author ON author.id=p.author',
'WHERE p.deleted IS NULL',
'ORDER BY p.created DESC',
'LIMIT $1'
].join(' ')
@pgsql.exec(q, [limit]).map do |r|
{
id: r['id'].to_i,
coordinates: r['coordinates'],
author: r['login'],
badges: r['badges'][1..-2].split(','),
created: Time.parse(r['created'])
}
end
Expand Down
1 change: 1 addition & 0 deletions objects/reviews.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def get(id)

def post(text)
raise Xia::Urror, 'Not enough karma to post a review' unless @project.author.karma.positive?
raise Xia::Urror, 'The review is too short' if text.length < 100
id = @pgsql.exec(
'INSERT INTO review (project, author, text) VALUES ($1, $2, $3) RETURNING id',
[@project.id, @project.author.id, text]
Expand Down
41 changes: 41 additions & 0 deletions test/test_badges.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

# Copyright (c) 2020 Yegor Bugayenko
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the 'Software'), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'minitest/autorun'
require_relative 'test__helper'
require_relative '../objects/xia'
require_relative '../objects/authors'

class Xia::BadgesTest < Minitest::Test
def test_attaches_badge
author = Xia::Authors.new(t_pgsql).named('yegor256')
projects = author.projects
project = projects.submit('github', "yegor256/takes#{rand(999)}")
badges = project.badges
text = 'jey'
badge = badges.attach(text)
assert_equal(2, badges.all.size)
assert(badges.all.include?(text))
assert(!badge.id.nil?)
badge.detach
end
end
11 changes: 11 additions & 0 deletions test/test_projects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,15 @@ def test_submits_project
assert(!project.id.nil?)
assert(!projects.recent.empty?)
end

def test_adds_badges_and_fetches_them
author = Xia::Authors.new(t_pgsql).named('yegor256')
projects = author.projects
project = projects.submit('github', "yegor256/takes#{rand(999)}")
badge = 'hello'
project.badges.attach(badge)
project.badges.attach('something')
assert(projects.recent[0][:badges].is_a?(Array))
assert(projects.recent[0][:badges].include?(badge))
end
end
4 changes: 3 additions & 1 deletion test/test_reviews.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ def test_submits_review
projects = author.projects
project = projects.submit('github', "yegor256/takes#{rand(999)}")
reviews = project.reviews
review = reviews.post('How are you?')
review = reviews.post(
'How are you? This is a nice long review to pass all possible quality checks of ours. Don\'t make it shorter.'
)
assert(!review.id.nil?)
assert(!reviews.recent.empty?)
end
Expand Down
13 changes: 11 additions & 2 deletions views/project.haml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
%a{href: "https://github.com/#{project.coordinates}"}
%img{src: iri.cut('/github-logo.svg'), style: 'vertical-align: middle; width: 1em;'}

%p
- project.badges.all.each do |b|
%kbd
&= b

- if the_author.karma >= 500
%form{action: iri.cut('/p').append(project.id).append('attach'), method: 'POST'}
%input{type: 'text', name: 'text', size: 25, placeholder: 'attach a new badge...'}
%button{type: 'submit'} Attach

- reviews.each do |r|
%p
%span.smaller.gray
Expand All @@ -19,9 +29,8 @@
&= r[:text].gsub('\n\n', '<br/><br/>')

- if the_author.karma >= 50
%form{action: iri.cut('/do-review'), method: 'POST'}
%form{action: iri.cut('/p').append(project.id).append('post'), method: 'POST'}
%fieldset
%input{type: 'hidden', name: 'project', value: project.id}
%label What do you think about it?
%textarea{name: 'text', style: 'width: 100%; height: 8em;'}
%button{type: 'submit'} Post
4 changes: 4 additions & 0 deletions views/recent.haml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
%a{href: iri.cut('/p').append(p[:id])}
&= p[:coordinates]
%br
- p[:badges].each do |b|
%kbd
&= b
%br
%span.smaller.gray
%span.item
%img{src: "https://socatar.com/github/#{p[:author]}/192-192", style: 'height: 1em; width: 1em; vertical-align: middle;'}
Expand Down

0 comments on commit b1b5f30

Please sign in to comment.