: Dev.Study - Official VSNS Repository since 2013.9.1
-
Gemfile ์ถ๊ฐ
gem 'rack-cors', :require => 'rack/cors' by hschoi -
config/application.rb
config.middleware.use Rack::Cors do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :options] end end
- items.js.coffee
$(document).on 'click', '.add_a_comment_link', ->
$(this).parent().parent().next().next().slideToggle()
false
$(document).on 'click', '.show_comments_link', ->
if $(this).hasClass('enabled')
$(this).next().slideToggle()
initTooltip = ->
$('.thumbnail').tooltip
placement: 'bottom'
$ ->
initTooltip()
$(document).on 'page:load', initTooltip
- communities.js.coffee
$(document).on 'click', "#add_a_community_link", ->
$(this).parent().next().slideToggle()
false
$(document).on 'click', "#community_header_info_link", ->
$(this).parent().parent().find('.info').slideToggle()
false
User๋ชจ๋ธ์username์์ฑ์ validation์ดpresence: true๋ก ์ง์ ๋์ด ์์ดusername๊ฐ์ดnil์ผ ๊ฒฝ์ฐ๋ ์๊ฒ ์ง๋ง, ์ด์ ์ ์ ์ ๋ฐ์ดํฐ์๋username๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ฐ์ํจ. ๋ฐ๋ผ์, ์๋์ ๊ฐ์ด ์กฐ์นํจ.
<%= link_to raw("<i class='icon-user'></i> #{current_user.try(:username)}" + " <span class='caret'></span>"), '#', class:'dropdown-toggle', data:{toggle:'dropdown'} %>
-
like ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด spinner ์ด๋ฏธ์ง๊ฐ ๋ณด์๋ค๊ฐ ์ฌ๋ผ์ง๊ฒ ํ์์ต๋๋ค.
-
์ด์คํ๋์ ๋์์ผ๋ก pageless ๊ธฐ๋ฅ ๋ฌธ์ ์ ํด๊ฒฐํจ
-
๊ธฐํ ๋๋จธ์ง Turbolinks ๋ฌธ์ ๋ฅผ ํด๊ฒฐํจ.
-
app/assets/javascripts/communities.js.coffee
``` $(document).on 'click', "#add_a_community_link", -> $(this).parent().next().slideToggle() false $(document).on 'click', "#community_header_info_link", -> $(this).parent().parent().find('.info').slideToggle() false ``` -
app/assets/javascripts/items.js.coffee
``` $(document).on 'click', '.add_a_comment_link', -> $(this).parent().parent().next().next().slideToggle() false $(document).on 'click', '.show_comments_link', -> if $(this).hasClass('enabled') $(this).next().slideToggle() initTooltip = -> $('.thumbnail').tooltip placement: 'bottom' $ -> initTooltip() $(document).on 'page:load', initTooltip ```
-
-
์ด์คํ๋์ ๋์์ผ๋ก pageless ๊ธฐ๋ฅ ๋ฌธ์ ์ ํด๊ฒฐํจ
-
in items/index.html
<div id='items' data-total-pages="<%= @items.total_pages %>" data-url="<%= items_path %>" data-loader-image="<%= image_path('load1.gif') %>"> <%= render @items %> </div> <!--To apply bottom-less pagination using pageless jQuery plugin--> <!--Gem : pageless-rails & will_paginate--> <%= will_paginate @items %> -
in items.js.coffee
initPageless = -> $items = $('#items') # items dom ์กด์ฌ์ฌ๋ถ ํ์ธ return unless $items.length # pageless ์ค์ ์ ๋ณด dom์์ ๊ฐ์ ธ์ค๊ธฐ opts = totalPages : $items.data('total-pages') url : $items.data('url') loaderMsg : 'Loading more pages...' loaderImage : $items.data('loader-image') # pageless ์์ $items.pageless opts # pageless ์ด๊ธฐํ resetPageless = -> $items = $('#items') return unless $items.length $.pagelessReset() $ -> initPageless() # Turbolink ์ด๋ฒคํธ๋ฅผ ํตํ ์ฒ๋ฆฌ $(document).on 'page:load', initPageless $(document).on 'page:before-change', resetPageless # ํ๋ฉด ์ ํ์ pageless ์ด๊ธฐํ
-
wmd editor์ turbolinks ๋ฌธ์ ๋ฅผ ์ด์คํ๋์ ๋์์ ๋ฐ์ ๊น๋ํ๊ฒ ํด๊ฒฐํ์์(editor.js)
//= require wmd/wmd //= require wmd/showdown var initializeWMDEditor = function () { new WMDEditor({ input: "item_description", button_bar: "editor-button-bar", preview: "editor-preview", output: "editor-output" }); $(".wmd-help-button").html("<a id='markdown-help-link'></a>"); $("#markdown-help-link").click(function(){ $("#editor-help-panel").slideToggle('fast'); return false; }); }; $(function () { // dom ready initializeWMDEditor(); }); // Turbolink ์ด๋ฒคํธ๋ฅผ ํตํ ์ฒ๋ฆฌ $(document).on('page:load', initializeWMDEditor); -
์ด์ ํจ๊ป jquery.tokeninput.js ์ญ์ turbolinks ๋ฌธ์ ๋ ํด๊ฒฐํ์์(items.js.coffee).
initTagInput = -> $tagInput = $('input[name="item[tag_list]"]') $tagInput.tokenInput "/items/tags.json", theme : 'facebook' tokenValue : 'name' allowFreeTagging : true prePopulate : $tagInput.data('tags') $ -> initTagInput() # Turbolink ์ด๋ฒคํธ๋ฅผ ํตํ ์ฒ๋ฆฌ $(document).on 'page:load', initTagInput
- facebook ํ๋กํ ์ด๋ฏธ์ง ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ ์ต์ด ๊ณ์ ์์ฑ์ ์ฌ์ฉ
- devise์์ ๊ธฐ๋ณธ ์ ๊ณตํ๋ ๋ก๊ทธ์ธํ๋ฉด(_links.erb)๋ฅผ ์์ ํ์ฌ ๋ก๊ทธ์ธ ํผ์ ๋ณ๊ฒฝ
- vsns_config.yml์ ์ค์ ํ์ผ๋ก ์ฌ์ฉ (ENVํ์์ ๋จ์ ๋ณด์)
- vsns_config.yml์ ์ถํ ignoreํด์ผํจ
- devise์ omniauth๋ฅผ ์ด์ฉํ์ฌ twitter/facebook ๋ก๊ทธ์ธ ์ฐ๋
- ๊ธฐ์กด์ ์๋ username column์ ๊ทธ๋๋ก ํ์ฉํ์ฌ "์ฌ์ฉ์์ด๋ฆ"์ผ๋ก ์ฌ์ฉ (unique)
- email์ ๋ณด์ฌ์ฃผ๋ ํ๋ฉด์ username์ผ๋ก ๋ฐ๊ฟ
- email์ ์์ ํ์ง ๋ชปํ๋๋กํจ (login id ์ด๋ฏ๋ก..)
- omniauth, omniauth-github๋ฅผ ์ฌ์ฉํด Github๋ก ๋ก๊ทธ์ธํ๊ธฐ ๊ธฐ๋ฅ ์ถ๊ฐ
- act_as_taggable_on ๋ฅผ ์ฌ์ฉํด Tagcloud ๊ธฐ๋ฅ์ ์ฌ์ด๋ธ์ ์ถ๊ฐ
-
config/initializers/devise.rb
config.omniauth :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: 'user,public_repo'
-
app/controllers/omniauth_callbacks.rb
- Github oauth callback์ฉ
-
ํ์๊ฐ์ , ํ์์ ๋ณด ์์ ๋ทฐ ์์
- Github oauth ์ password ํ๋ ์ ์ธ์ฒ๋ฆฌ
-
์๋จ ๋ค๋น๊ฒ์ด์ ๋ฐ Github๋ก ๋ก๊ทธ์ธ ๋งํฌ ์ถ๊ฐ
-
app/models/user.rb
- omniauth ๊ด๋ จ ์ค์ ๋ฐ ํด๋์ค & ์ธ์คํด์ค ๋ฉ์๋ ์ถ๊ฐ
- omniauth๋ฅผ ํตํด ๋ค์ด์จ user thumbnail์ ์ ์ฅํ๋๋ก ๊ตฌํ
-
development ํ๊ฒฝ annotate gem ์ถ๊ฐ
- ๊ฐ model์ table schema ์ ๋ณด comment๋ก ์ถ๊ฐํด์ฃผ๋ gem
- ์ฐธ๊ณ ๋งํฌ : annotate_models
-
development & test ํ๊ฒฝ dotenv-rails gem ์ถ๊ฐ
-
์ฐธ๊ณ ๋งํฌ : dotenv
-
local ๊ฐ๋ฐ ํ๊ฒฝ์์ ENV ๋ฅผ rails server ์์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ค
-
project_root/.env ํ์ผ์ ๋ง๋ค์ด ์ฌ์ฉ
-
example vsns/.env
GITHUB_KEY=github์์ ๋ฐ๊ธ๋ฐ์ app์ key GITHUB_SECRET=github์์ ๋ฐ๊ธ๋ฐ์ app์ secret
-
-
app/asset/stylesheet.css.scss
- Tag count ์ ๋ฐ๋ผ ์ ์ฉ๋ ์คํ์ผ ์ํธ ์ ์
-
app/view/layout/shared/_sidebar.html.erb
- Tag cloud ๊ฐ ๋ ธ์ถ๋ layer ์ถ๊ฐ
-
app/view/layout/shared/_my_tag_cloud.html.erb
- acts_as_taggable_on์์ ์ ๊ณตํ๋ tag_cloud ํฌํผ ๋ฉ์๋๋ฅผ ํ์ฉ
-
app/model/user.rb
- owned_my_tag_counts ๋ฉ์๋ ์ถ๊ฐ
- acts_as_taggable_on์ด ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ์์ดํ ์ Tag ๋ฅผ ๊ธฐ์ค์ผ๋ก Cloud ์์ฑํ๋ ๊ฒ์ ํน์ ์ ์ ๊ธฐ์ค์ผ๋ก ๋ณ๊ฒฝ
-
ํน์ด์ฌํญ
- ๋ก์ปฌ ํ๊ฒฝ์์ github๋ก login์ ํ ์คํธํ๊ธฐ ์ํด .envํ์ผ์ ํด๋น ์ ๋ณด๋ฅผ ์ธํ ํด์ค์ผ ํจ
- .env ํ์ผ์ ๋ณด์์ gitignore์์ ์ ์ธ์ํด
- heroku deploy ์์ GITHUB_KEY์ GITHUB_SECRET์ ENV๋ก ์ค์ ํด์ค์ผํจ
-
summernote-rails ์ ฌ์ wmd-rails ์ ฌ์ผ๋ก ๋์ฒดํ์์ต๋๋ค.
-
wmd-rails ์ ฌ์ wmd ๋งํฌ๋ค์ด ์๋ํฐ๋ฅผ assets pipeline์์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ ฌ์ผ๋ก ๋ง๋ ๊ฒ์ ๋๋ค.
-
wmd-rails ์ ฌ์ ์ฌ์ฉํ ๋์ ์ฃผ์์ ์ wmd/wmd.js ํ์ผ๋ด์ WMDEditor ๊ฐ์ฒด๋ editor-input ์์ฑ์ผ๋ก ์ง์ ๋ DOM ๊ฐ์ฒด๊ฐ ์๋ ๊ฒฝ์ฐ ์คํฌ๋ฆฝํธ ์๋ฌ๋ฅผ ๋ฐ์ํฉ๋๋ค. ๋ฐ๋ผ์ ํผ ๋ทฐ ํ ํ๋ฆฟ์์๋ง wmd/wmd.js ํ์ผ์ ํฌํจํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ์ํด items ์ปจํธ๋กค๋ฌ ์ ์ฉ ๋ ์ด์์ ํ์ผ์ธ items.html.erb ํ์ผ์ ๋ง๋ค์๊ณ , ์ฌ๊ธฐ์ editor.js ์ editor.css ํ์ผ์ ํฌํจํ๋๋ก ํ์์ต๋๋ค.
-
items์ show ๋ทฐ ํ ํ๋ฆฟ์์๋ ๋งํฌ๋ค์ด ์ปจํ ์ธ ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด wmd/showdown.js ํ์ผ์ ์ฌ์ฉํฉ๋๋ค. ๋ฐ๋ผ์ ์ด ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ application.js manifest ํ์ผ์ ํฌํจํด ์ฃผ์์ต๋๋ค.
-
์ด๋ฌํ ์กฐ์น๋ฅผ ์ทจํ๊ธฐ ์ํด์๋ manifest ํ์ผ(applicaiton.js, application.css) ํ์ผ๋ด์
require_tree .๋ผ์ธ์ ์ ๊ฑฐํ๊ณ ํ์๋ก ํ๋ ๋ชจ๋ ํ์ผ์ ๋ช ์์ ์ผ๋ก ํฌํจํด์ผ ํฉ๋๋ค. ์ด์ ์ฐ๊ดํ์ฌ editor.js์ editor.css ํ์ผ์ precompile ์์ ํฌํจ์ํค๊ธฐ ์ํด์ ์๋์ ๊ฐ์ด application.rb ํ์ผ์ ์ฝ๋๋ผ์ธ์ ์ถ๊ฐํด ์ฃผ์ด์ผ ํฉ๋๋ค.config.assets.precompile += %w(editor.js editor.css)
3be2c5a30f5e6dabf097712ba7600d4774d5be01
- commnities.html.erb์ _community.html.erb ํ์ผ์ refactoringํ์์ต๋๋ค.
-
item๊ธ ์ ๋ก๋ํ ๋ ์๋ฌด๊ฒ๋ ์ ๋ ฅํ์ง ์๊ณ Saveํ๋ฉด @communities_joined๊ฐ nil๋ก ๋ฐ์ํ๋ ๋ฒ๊ทธ๋ฅผ ์์ ํ์ต๋๋ค.
ItemController before_filter :set_communities_joined ์ต์ ์ update, create๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
-
ํ๊ทธ ์ ๋ ฅ์์ ์๋์์ฑ์ด ๋๋๋ก ๊ฐ์ ํ์์ต๋๋ค.
Dev Ruluํ์์ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ผ๋ก์จ jquery.tokeninput.js ์ธ js, cssํ์ผ๋ค์ ์ด์ฉํ์์ต๋๋ค.
2013๋ 9์ 7์ผ => summernote ์๋ํฐ(์์ง์๊ทธ ์๋ํฐ)๋ฅผ wmd editor(๋งํฌ๋ค์ด ์๋ํฐ)๋ก ๊ต์ฒด
-
items ์ปจํธ๋กค๋ฌ์ form ๋ทฐ์์๋
description์์ฑ์ ๋ํด์ ํ์ฌ summernote ์์ง์๊ทธ ์๋ํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธ์ ์์ฑํ๋๋ก ๊ตฌํ๋์ด ์์ต๋๋ค. -
์ด๊ฒ์ ๋งํฌ๋ค์ด ์๋ํฐ์ธ
wmd์๋ํฐ๋ก ๊ต์ฒดํ์ฌ ๊ตฌํํด ๋ณด์์ต๋๋ค. ์ด๊ฒ์์์ฅ์์ฅ์ ๋ ๋ฏธ์ ์ค์ ํ๋๋ก ์ ๊ฐ ๋ณ๋๋ก ๊ตฌํํ ๊ฒ์wmd_editor๋ผ๋ branch๋ฅผ ๋ง๋ค์ด pushํด ๋์์ต๋๋ค.
-
.idea ์ tmp ๋๋ ํ ๋ฆฌ๋ ๋ถํ์ํ ๋๋ ํ ๋ฆฌ๋ผ์ ์ ๊ฑฐํ์ต๋๋ค.
-
์ ๋ ๋ฆฌ๋๊ป์๋
upstream์ด๋ผ๋remote branch๋ฅผ ํ๋ ์ถ๊ฐํ์๊ณ https://github.com/dev-study/vsns.git ๋ก ์ง์ ํด ์ฃผ์ ํ์git pull upstream masterํด ์ฃผ์๋ฉด ๋ฉ๋๋ค. ์์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.vsns $> git remote add upstream https://github.com/dev-study/vsns.git vsns $> git checkout master vsns %> git pull upstream master -
์ด๋ ๊ฒ ํ๋ฉด dev-study/vsns ์ ์ฅ์์ master branch๋ฅผ ๋ณธ์ธ์ ๋ก์ปฌ master ๋ธ๋์น๋ก pull ํ ์ ์๊ฒ ๋๋ ๊ฒ์ด์ฃ . ๊ทธ๋ฆฌ๊ณ ๋์ ์ ๋ ์ ์ฅ์๋ก git push origin master ํ์๋ฉด ์ต์ข ์ ์ผ๋ก dev-study/vsns ์ ์ฅ์์ ์ ๋ฐ์ดํธ๋ ๋ด์ฉ์ด ์ ๋ ์ ์ฅ์๋ก ๊ฐ์ ธ์ ๋จธ์งํ๊ฒ ๋๋ ๊ฒ์ ๋๋ค.
-
2013๋ 9์ 5์ผ ์์
Midnight Hangout Code Review์์ ์งํ๋์๋refactoring๊ฒฐ๊ณผ๋ฅผmaster branch๋ก ๋จธ์งํ๊ณgit tag๋ฅผv0.1.3์ผ๋ก ์์ฑํ์์ต๋๋ค. -
๊ณต์งํ ๋ฐ์ ๊ฐ์ด ์ด์ (2013.9.6 06:00)๋ถํฐ๋ ๊ฐ ์ ๋ ๋ฆฌ๋๋ ์ด ๋ฒ์ (v0.1.3)์
forkํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ ๋ ๋ฉค๋ฒ๋ ์ ๋ ๋ฆฌ๋์ ์ ์ฅ์๋ฅผgit cloneํฉ๋๋ค.
์ ๋ฆฌํ๋ฉด,
-
๊ฐ ์ ๋์ ๋ฆฌ๋๋ dev-study/vsns ์ ์ฅ์(v0.1.3) ๋ฅผ forkํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ ๋ ๋ฉค๋ฒ๋ ์ ๋ ๋ฆฌ๋์ ์ ์ฅ์๋ฅผ clone ํฉ๋๋ค. ์ด์ ๋ํด์๋ ์ด์คํ๋์ด ์ฌ๋ ค ์ฃผ์ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ๋ฉด fork ๊ธฐ๋ฅ์ ์ดํดํ๋๋ฐ ๋ง์ ๋์์ด ๋ ๊ฒ์ ๋๋ค. http://blog.outsider.ne.kr/865
-
์ ๋ ๋ฉค๋ฒ๋ค์ ๊ฐ์์ branch๋ฅผ ์์ฑํ์ฌ ์ฝ๋ฉ ์์ ์ ํฉ๋๋ค.
$ git checkout -b [๋ณธ์ธ์ branch๋ช ] -
์ ๋ ๋ฉค๋ฒ๋ ๋ณธ์ธ์ ์ฝ๋ฉ ์์ ์ด ์๋ฃ๋๋ฉด clone ๋ฐ์๋ ๋ฆฌ๋์ ์ ์ฅ์(github ๋ด์ ๋ฆฌ๋ ์ ์ฅ์)๋ก ์ปค๋ฐ๋ด์ฉ์ push ํ๋๋ฐ, ์๋์ ๊ฐ์ ์์๋ก ์์ ์ ํฉ๋๋ค.
$ git add . $ git commit -m "some commit messages" $ git push origin [๋ณธ์ธ์ branch๋ช ] -
์ด์ ์ ๋์ ๊ฐ ๋ฉค๋ฒ๊ฐ ํ์ ์ ํตํด์ ์์ ์ ์๋ฃํ๋ฉด, ์ ๋ ๋ฆฌ๋๋ ์ ๋๋ฉค๋ฒ๋ค์ branch๋ฅผ ๋จธ์งํ๊ณ (์ด๋ ์ฝ๊ฐ์ ์ปค๋ฐ ์ถฉ๋์ด ์์ ์ ์๋๋ฐ ๋ฆฌ๋๋ ์ด๋ฌํ ์ถฉ๋๋ถ๋ถ์ ํด๊ฒฐํด์ผ ํจ) ์ ๋ ๋ฆฌ๋๋ ์ต์ข
master branch๋ฅผpull requestํ๋ฉด ๋ฉ๋๋ค. ์ ๋ ๋ฆฌ๋๋ ์๋์ ๊ฐ์ ์์๋ก ์์ ์ ํฉ๋๋ค.$ git pull origin [์ ๋๋ฉค๋ฒ์ branch๋ช ] # ์ ๋๋ฉค๋ฒ์ ์ ๋งํผ ... $ git checkout master $ git merge [์ ๋๋ฉค๋ฒ์ branch๋ช ] # ์ ๋๋ฉค๋ฒ์ ์ ๋งํผ ... -
๋ฉค๋ฒ branch์ ๋จธ์ง ์์ ์ด ์๋ฃ๋๋ฉด ์ ๋ ๋ฆฌ๋๋ ์๋์ ๊ฐ์ด git push ๋ช ๋ น์ ์ํํฉ๋๋ค.
$ git push origin master -
์ด์ ์ ๋ ๋ฆฌ๋๋ github์ ์ ์ํ์ฌ ๋ณธ์ธ์ ๊ณ์ ์ผ๋ก forkํ dev-study/vsns ์ ์ฅ์๋ก
PULL REQUEST๋ฅผ ํฉ๋๋ค. ์ฌ๊ธฐ ๊น์ง์ ์์ ์ ๊ฐ ์ ๋์์ ์ํํ๋ ๊ฒ์ ๋๋ค. -
์ด๋ ๊ฒ 8๊ฐ์ ์ ๋์์
pull request๊ฐ ๋ค์ด์ค๋ฉด ์ปค๋ฐํฐ๋ก ์ง์ ๋ ๋ฉค๋ฒ๋ค์pull request์ปค๋ฐ๋ค์ ๊ฒํ ํ๊ณ ์ต์ข master ๋ฒ์ ์ผ๋กmergeํ๋ฉด ๋ฉ๋๋ค. -
์ ์ฐจ์์ ๋ฌธ์ ๊ฐ ์๋ค๊ณ ํ๋จ๋์๋ฉด ์ธ์ ๋ ์ง ์ฐ๋ฝ์ ์ฃผ์๊ธฐ ๋ฐ๋๋๋ค.
- ์ต์ข tag ๋ฒ์ ์ v0.1.2 ์ ๋๋ค. v0.1.1 ๋ฒ์ ์ ๋ฒ๊ทธ์์ ํ ๊ฒ์ด ๋ฐ์์ด ์๋์์ต๋๋ค. ์ฃ์กํฉ๋๋ค.
- ์ต์ข tag ๋ฒ์ ์ v0.1.1 ์ ๋๋ค.
1. ItemController์ show, edit, new ์ก์ ์์ before_filter๋ฅผ ํตํด set_communities_joined๋ฅผ ์ฒ๋ฆฌํ๋๋ก ํจ
- ์ด์ : layouts/shared/_my_communities.html.erb์์ @communities_joined๋ฅผ ์ฌ์ฉํ์ฌ community ๋ชฉ๋ก์ ๊ตฌ์ฑํ๋๋ฐ, show.html, edit.html, new.html ์์๋ @communities_joined๊ฐ nil์ด์ด์ ์ค๋ฅ ๋ฐ์
- ๊ด๋ จ ํ์ผ
- controllers/items_controller.rb
- ๊ด๋ จ ํ์ผ
- view/communities/create.js.erb
- view/layouts/shared/_my_communities.html.erb
- controllers/communities_controller.rb
-
์์ ๋ถ๋ถ: @communities_joined.size๊ฐ 0์ธ ๊ฒฝ์ฐ๋ ul ์์ญ์ ์์ฑํ๋๋ก ์์ .
-
์ด์ : view/communities/create.js.erb์์๋ #community_list ์์ญ์ ์๋ก ์์ฑํ cummunity๋ฅผ ์ถ๊ฐํ๋๋ก ๊ตฌํ๋์ด ์์. communities.size๊ฐ 0์ธ ๊ฒฝ์ฐ์๋ #community_list ์์ญ์ ์์ฑํด์ฃผ์ด์ผ ์ด ๋ถ๋ถ์ด ์ ์์ ์ผ๋ก ๋์ํจ
-
๊ด๋ จ ํ์ผ
- view/layouts/shared/_my_communities.html.erb
: as of 2013๋ 9์ 1์ผ, 18:45am
-
์ด์ Big Pie ํํ๋ก์ ํธ๋ก ๊ณต๋(?) ๊ฐ๋ฐ๋
vsns Blog project๋ฅผ master branch์ ๋จธ์งํ๊ณ v0.1.0 ํ๊ทธ๋ฅผ ๋ฌ์์ github์ pushํ์ต๋๋ค. -
์๋น ๋ถ๋ถ์ refactoring ๊ณผ์ ์ด ํ์ํ๊ณ , ์ฝ๊ฐ์ ๋ฒ๊ทธ๊ฐ ์์ ์ ์์ต๋๋ค. ์ด๋ฌํ ๋ถ๋ถ์ ๋ํ ๋ฉค๋ฒ๋ค์ ์์ง์ด ํ์ํฉ๋๋ค.
-
๋ณธ ํ๋ก์ ํธ์ ์์ค๋ฅผ ๊ณต์ ํ์ฌ ์์ ์ ํ๊ณ ์ ํ ๋๋ ์ด์ ๊น์ง ๋ฐฉ์๊ณผ๋ ๋ฌ๋ฆฌ https://github.com/dev-study/vsns ๋ก ์ ์ํ์ ํ ๋ธ๋ผ์ฐ์ ํ๋ฉด ์ฐ์ธก์๋จ์ ์๋
Fork๋ฒํผ์ ํด๋ฆญํ์ฌ ์์ ์ ๊ณ์ ์ผ๋ก forking ํ์ ํ์ ์์ ์ ํ์๊ณ ๋ณ๊ฒฝ๋ด์ฉ์ ๋ํ ๋จธ์ง๋ฅผ ์ํ ๊ฒฝ์ฐ์๋pull request์์ ์ ํตํ์ฌ ํ์ฌ ์ฃผ์๊ธฐ ๋ฐ๋๋๋ค. -
Forking๊ณผ Pull Request์ ๋ํ ์ฌํญ github ํํ์ด์ง๋ฅผ ์ฐธ์กฐํ์ ๋ฐ๋๋๋ค.
-
Community๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์์ต๋๋ค. ์ด๋ฅผ ์ํดCommunity๋ฆฌ์์ค๋ฅผ ์ถ๊ฐํ๊ณ , User์Community๋ชจ๋ธ์ ์ฐ๊ฒฐ์ ๋ค๋๋ค๋ก ์ฐ๊ฒฐํ๊ธฐ ์ํด์Associate๋ผ๋ join ๋ชจ๋ธ์ ์ถ๊ฐํ์ต๋๋ค.class Community < ActiveRecord::Base has_many :associates, dependent: :destroy has_many :users, :through => :associates has_many :items, :through => :users end class Associate < ActiveRecord::Base belongs_to :user belongs_to :community before_save :default_values def default_values self.access_type = "member" end end -
๊ทธ๋ฆฌ๊ณ
User๋ชจ๋ธ์๋ ์๋์ ๊ฐ์ด ๊ด๊ณ์ค์ ์ ์ถ๊ฐํ์ต๋๋ค.in app/models/user.rb
has_many :associates has_many :communities, :through => :associates -
Community ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Community์์ฑ๊ธฐ๋ฅ : ์ฐ์ธกMy Join Communities์์ ฏ์ ์๋์ ๋ณด๋ฉดCreate a community๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๋ฐ๋ก ์๋์ ์ ๋ ฅ์ฐฝ์ด ๋ณด์ด๊ฒ ๋ฉ๋๋ค. ์ ๋ ฅ์ฐฝ์ ์ปค๋ฎค๋ํฐ๋ช ์ ์ ๋ ฅํ๊ณ ์ํฐํค๋ฅผ ๋๋ฅด๋ฉด ajax ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌMy Join Communities๋ชฉ๋ก์ ์ถ๊ฐ๋ฉ๋๋ค. ์ด ํญ๋ชฉ์ ๋ํ ์ ํจ์ฑ ๊ฒ์ฆ์ ๋๊ฐ์ง์ ๋๋ค.:presence => true,:uniqueness => true.My Join Communities์๋ ๋ณธ์ธ์ด ๋ฉค๋ฒ๋ก ๋ฑ๋ก๋ Community๊ฐ ๋ณด์ด๊ฒ ๋๋๋ฐ, 10๊ฐ๊ฐ ๋์ด๊ฐ ๋๋ 10๊ฐ๋ง ํ์๋๊ณ ๊ทธ ์๋์ 10๊ฐ๋ฅผ ๋บ ์๋ฅผmore...๋งํฌ๋ก ๋ณด์ฌ์ค๋๋ค. ์ด ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ์ ์ฒด ๋ชฉ๋ก์ ๋ณผ ์ ์๋ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.- ๊ฐ
Communityํญ๋ชฉ์ ์ค๋ฅธ์ชฝ์๋ ๊ฐ์ (join)/ํํด(leave) ๋งํฌ๊ฐ ์์ด์ ํธ๋ฆฌํ๊ฒ ๊ฐ์ ๋ฐ ํํด๊ฐ ๊ฐ๋ฅํ๋๋ก ํ์ต๋๋ค. My Join Communities์ ๋ณด์ด๋ ๊ฐCommunity๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๊ฐ์ ๋ ๋ชจ๋ ๋ฉค๋ฒ์items๋ค์ด ๋ณด์ฌ์ง๋๋ค.- ์๋จ ๋ฉ๋ด ์ค
My Communities๋ ๋ณธ์ธ์ธ ๊ฐ์คํ Community ๋ชฉ๋ก์ ๋ณด์ฌ ์ค๋๋ค.
-
๋ณ์ฉํ๋์ด
tagsinput-rails์ ฌ์ ์ด์ฉํ์ฌ ์ฝ๋ฉํด ์ฃผ์ ๋ถ๋ถ์bootstrap-tagsinput-rails์ ฌ์ผ๋ก ๋์นํ์ฌ ๋ณ๊ฒฝํ์์ต๋๋ค. ํ์ฌ ํ๋ก์ ํธ์์๋Twitter Bootstrap์ ์ฌ์ฉํ๊ณ ์์ด์ ์คํ์ผ๋ง์ด ์ด์ธ๋ฆฌ ์๋๊ตฐ์. ๊ทธ๊ฒ ๋ง๊ณ ๋ ์ ๋์ํฉ๋๋ค. -
๊ทธ๋์ ๊ฒ์ํ๋๋
bootstrap-tagsinput์ด๋ผ๋ jQuery plugin์ด ์์ด์ ์ด๊ฒ์ assets pipeline์ ์ด์ฉํ์ฌ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ ฌ์ผ๋ก ๋ง๋ค์์ต๋๋ค. https://github.com/rorlab/bootstrap-tagsinput-rails
Original Git source - https://github.com/timschlechter/bootstrap-tagsinput
To gemify the assets of bootstrap-tagsinput jQuery plugin for Rails >= 3.1
Designed for Bootstrap 2.3.2 and 3
Add this line to your application's Gemfile:
gem 'bootstrap-tagsinput-rails'
And then execute:
$ bundle
Or install it yourself as:
$ gem install bootstrap-tagsinput-rails
in app/assets/application.js
//= require bootstrap-tagsinput
in app/assets/application.css
*= require bootstrap-tagsinput
in form view, you should add data-role='tagsinput' within input tag as the follows: for example, in simple-form view template,
<%= f.input :tag_list, input_html:{data:{role:'tagsinput'}} %>
That's it
-
Comment๊ฐ ํ๋ ์ถ๊ฐ๋๋ฉด ํด๋น Item์ ๋ชจ๋ธ ๊ฐ์ฒด์ updated_at ๋ ์ง๊ฐ ๊ฐฑ์ ๋์ด ํ์ด์ง์ ์ต์๋จ์ ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
in app/models/comment.rb
belongs_to :commentable, polymorphic: true, touch: true์ด๋ฅผ ์ํด์ touch: true ์ต์ ์ ์ฃผ๋ฉด ๋ฉ๋๋ค.
-
Gemfile์ authority & rolify ๋๊ฐ์ ์ ฌ ์ถ๊ฐํ๊ณ bundle install ํฉ๋๋ค.
-
์ค๋น ์์ ์ ์๋์ ๊ฐ์ด ํฉ๋๋ค.
# Create the ApplicationAuthorizer from Authority $ rails generate authority:install # Create the Role class from rolify $ rails generate rolify:role # Create related tables $ rake db:migrate -
๊ถํ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด์๋ ์๋์ ์๋ฃ๋ฅผ ์ฐธ์กฐํ๊ธฐ ๋ฐ๋๋๋ค.
- Devise + Authority + Rolify : https://github.com/EppO/rolify/wiki/Using-rolify-with-Devise-and-Authority
-
์ด์ ๋ถํฐ๋ ๋ณธ์ธ์ด ์์ฑํ ๊ธ์ด๋ ๋๊ธ์ ๋ํด์๋ง ๊ถํ์ด ์ฃผ์ด์ง๋๋ค. ๋ค๋ฅธ ์ฌ๋์ ๊ธ์ด๋ ๋๊ธ์ ์์ ํ๊ฑฐ๋ ์ญ์ ํ ์ ์์ต๋๋ค
-
Comment ๋ชจ๋ธ์ ์์ฑ. ์ด ๋ชจ๋ธ๋ polymorphic association์ผ๋ก ์ด๋ ํ ๋ชจ๋ธ์๋ ๋ถ์ผ ์ ์๋๋ก ํ์์ต๋๋ค.
$ rails g model comment user:references commentable:references{polymorphic} body:text invoke active_record create db/migrate/20130827234430_create_comments.rb create app/models/comment.rb invoke test_unit create test/models/comment_test.rb create test/fixtures/comments.yml $ rake db:migrate == CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0101s == CreateComments: migrated (0.0101s) ======================================== -
[์ฃผ์] simple_form ์ ฌ ์ค์นํ ๋ค์์ $ rails g simple_form:install --bootstrap ๋ช ๋ น์ ์ํํ์๋์ง ๋ชจ๋ฅด๊ฒ ๋ค์. simple_form์ class๋ฅผ
form-vertial๋ก ๋ณ๊ฒฝํด์ผ ํ ํ์๊ฐ ์์ง๋ง, ์ ์ฉ์ด ๋์ง ์๋๊ตฐ์. ๊ทธ๋์ ๋ค์ $ rails g simple_form:install --bootstrap ๋ช ๋ น์ผ๋ก ๋ฅ์ด์ฐ๊ธฐ ํ์ต๋๋ค. ์ด์ default๊ฐform-vertical๋์์ต๋๋ค. ๊ทธ๋์ ์ถ๊ฐ ์์ ์ views/devise/ ๋๋ ํ ๋ฆฌ ์๋์ registraion๊ณผ session ๋๋ ํ ๋ฆฌ์ ์๋ simple_form์ class๋ฅผform-horizontal๋ก ๋ณ๊ฒฝํด ์ฃผ์ด์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ comment ํผ์์๋ ๋ณ๋์ class๋ฅผ ์ง์ ํ ํ์๊ฐ ์๊ฒ ๋์์ต๋๋ค. -
Comment ๋ชจ๋ธ์ ๋ง๋ค์์ผ๋ ์ปจํธ๋กค๋ฌ์ ๋ทฐ ํ ํ๋ฆฟ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
in app/controllers/comments_controller.rb
class CommentsController < ApplicationController def create @commentable = comment_params[:commentable_type].classify.constantize.send('find',comment_params[:commentable_id]) comment_params[:user_id] = current_user.id flash[:notice] = "A comment was successfully created." respond_to do |format| if @comment = @commentable.comments.create(comment_params) format.html { redirect_to @comment, notice: 'Comment was successfully created.' } format.json { render json: @comment, status: :created, location: @comment } format.js else format.html { render action: "new" } format.json end end end def destroy comment = Comment.find(params[:id]) @commentable = comment.commentable_type.classify.constantize.send('find', comment.commentable_id) @comment = @commentable.comments.find(params[:id]) @comment.destroy flash[:alert] = "A comment was successfully deleted." respond_to do |format| format.html { redirect_to items_url, alert: 'Comment was successfully deleted.' } format.json { head :ok } format.js end end private def comment_params params.require(:comment).permit(:body, :user_id, :commentable_id, :commentable_type) end end -
์ฌ๊ธฐ์๋ ์ ์ ์ ๋๋ฐ๋ก ์ฐจ๋ ค์ผ ํฉ๋๋ค. polymorphic ๊ด๊ณ๋ก comment ๊ฐ์ฒด๋ค์ ๋ค๋ฃจ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์์ ์ฝ๋ ์ค์๋ ์์ํ ๊ฒ๋ค์ด ๋ณด์ ๋๋ค. classify๋ constantize๋ ํ๋ ๊ฒ๋ค์ด์ฃ . commentable_type์ผ๋ก ๋์ด์ค๋ ๊ฐ์ ํด๋์ค๊ฐ ์๋๋ผ ๊ทธ์ ๋ฌธ์ ์ ๋ถ๊ณผํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ฐ๋ฆฌ๋ ์ค์ ํด๋์ค๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ๋๊ฒจ ๋ฐ์ ๋ฌธ์์ด์ ๊ฐ์ง๊ณ ํด๋์ค๋ฅผ ๋ง๋ค์ด์(classify), ์์ํ(constantize)ํ๋ ๊ณผ์ ์ ๊ฑฐ์ณ์ผ ํฉ๋๋ค. ์ด๋ ๊ฒ ๋ง๋ค์ด์ง ํด๋์ค ์์์ ๋ํด์ send ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ finder ๋ฉ์๋๋ฅผ ์ธ์๋ก ๋๊ฒจ ์ฃผ๋ฉด runtime์์ ๋ฌธ์์ด์ ๊ฐ์ง๊ณ ์ค์ ํด๋์ค์ ๋ฉ์๋๋ฅผ ์คํํ ์ ์๊ฒ ๋๋ ๊ฒ์ ๋๋ค. ์กฐ๊ธ ์ด๋ ต์ฃ ? ใ ใ ใ .
-
๋๋จธ์ง comment์ ๋ํ ๋ทฐ ํ ๋ธ๋ฆฟ์ ๋ง๋ ๊ฒ์ ๋ณ๋์ ์ค๋ช ์ด ํ์์์ ๊ฒ ๊ฐ์ต๋๋ค.
-
ใ ใ ใ . ๋๋์ จ์ฃ ? ๋ค๋ฆ์ด ์๋๋ผ pageless ๊ตฌํํ๋๋ฐ, ์ ์ ํ์ํ views/items/index.js.erb ํ์ผ์ด ์๊ตฐ์. ์ถ๊ฐํด์ ๋ฐฐํฌํ์ต๋๋ค.
in views/items/index.js.erb
$("#items").html("<%= j render(@items) %>");
: as of 2013๋ 8์ 28์ผ, 7:00am (#2)
-
์ด ์๊ฐ ์ดํ์๋ if Rails.env == 'development' ์ธ ๊ฒฝ์ฐ, ๋ํดํธ ๊ฐ๋ฐ๋ชจ๋ DB๋ sqlite3๋ก, carrierwave ์ ์ฅ์๋ ๋ก์ปฌ ํ์ผ์์คํ ์ ๋๋ค.
-
๋ก์ปฌ์์๋ production ๋ชจ๋๋ฅผ ๋๋ ค ๋ณผ ์ ์๋๋ฐ, ์ด ๋๋ ๋๊ตฐ๋ฐ๋ฅผ ์ฝ๋ฉํธ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋ณ๊ฒฝํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
- config/initializer/carrierwave.rb ํ์ผ ์ค์์ 2-13 ๋ผ์ธ์ ์ฃผ์์ฒ๋ฆฌํด ์ฃผ์๋ฉด ๋ฉ๋๋ค.
- app/uploaders ๋๋ ํ ๋ฆฌ์ ์๋ ๊ฐ ํ์ผ๋ค์ ์ด์ด์(ํ์ฌ 2๊ฐ์ ์ ๋ก๋๊ฐ ์์ต๋๋ค), storage :file ์ผ๋ก ๋ณ๊ฒฝํด ์ฃผ์๊ณ if ์กฐ๊ฑด์ ์ ์ฝ๋ฉํฐ ์ฒ๋ฆฌํด ์ฃผ๋ฉด ๋ฉ๋๋ค. ์ต์ข ์ ์ผ๋ก๋ ์๋์ ๊ฐ์ด ๋ฉ๋๋ค.
# Choose what kind of storage to use for this uploader: storage :file # storage :fog #if Rails.env == 'production' # storage :aws #else # storage :file #end -
๊ทธ ๋์ ๋ค๋ฅธ ๋ฉค๋ฒ๋ค์ ๊ณ ์ถฉ ์ ์์ง ๋ชปํ ์ ์ฃ์ก์ค๋ฝ๊ฒ ์๊ฐํฉ๋๋ค. ์ด์ ๋ก์ปฌ ๊ฐ๋ฐํ๊ฒฝ์์๋ ํํ๋ก์ ํธ๋ฅผ ์ฝ๊ฒ ์ ํ ํด์ ํ๋ก์ ํธ ๊ฐ๋ฐ์ ์ฝ๊ฒ ํ ์ ์์ ๊ฒ์ ์๊ฐํฉ๋๋ค. ๋ฌธ์ ์ ์ด ๋ฐ์ํ๋ฉด ์ ์๊ฒ ์ฐ๋ฝ์ ์ฃผ์๋ฉด ๋ฉ๋๋ค. rorlab@gmail.com
pageless-rails์ ฌ์ ์ถ๊ฐํ์ต๋๋ค. ์ด ์ ฌ์will_paginate์ ฌ์ ์ด์ฉํ์ฌ pagination์ ๊ตฌํํ ์ํ์์ bottom-less ๋๋ endless pagination์ด ๋๋๋ก ๋์ ์ฃผ๋ jquery plugin์ ๋๋ค.
in Gemfile,
gem 'pageless'
์ด๋ฏธ will_paginate ์ ฌ์ ์ค์น๋์ด ์์ผ๋ ์ฌ๊ธฐ์๋ ์ถ๊ฐํ ํ์๊ฐ ์์ต๋๋ค. ์ด์ Gemfile์ ์ ์ฅํ bundle install ํฉ๋๋ค.
in app/assets/javascripts/application.js, add the following code line.
//= require jquery.pageless
in app/helpers/application_helper.rb
def pageless(total_pages, url=nil, container=nil)
opts = {
:totalPages => total_pages,
:url => url,
:loaderMsg => 'Loading more pages...',
:loaderImage => image_path('load.gif')
}
container && opts[:container] ||= container
javascript_tag("$('#items').pageless(#{opts.to_json});")
end
์์ ๊ฐ์ด application ํฌํผ ํ์ผ์ ์ถ๊ฐํ๋ฉด erb ์ฝ๋ฉ์์ pageless ๋ฉ์๋๋ฅผ ํธํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ pageless pagination ์ ์ํ๋ view template ํ์ผ(views/items/index.html.erb)์์ ์๋์ ๊ฐ์ด ์ถ๊ฐํด ์ค๋๋ค.
<div class="items">
<%= render @items %>
</div>
<%= will_paginate @items %>
<%= pageless(@items.total_pages, items_path, '#items') %>
ํํธ, items_controller.rb ํ์ผ์ index ์ก์ ์์๋ @items ๋ณ์์ paginate ๋ฉ์๋๋ฅผ ํธ์ถํด ์ค๋๋ค.
@items = @items.order(updated_at: :desc).paginate(page: params[:page], per_page: 10)
- ์ ฌ ์ถ๊ฐ : slimbox2-rails v2.05.2 (https://github.com/rorlab/slimbox2-rails)
- ์ด ์ ฌ์ ์ด๋ฏธ์ง ํ์ผ์ lightbox ๋ก ๋ณผ ์ ์๊ฒ ํด ์ค๋๋ค. ๊ทธ๋ฆฌ๊ณ ํน์ ํ์ด์ง์ ๋งํฌ ์ค์ rel ์์ฑ์ด 'light-xxx' ์ ๊ฐ์ด ๋์ผํ ๊ฒฝ์ฐ๊ฐ ์ฌ๋ฌ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ lightbox์์ ์ข์ฐ ํ์ดํ๋ ๋ง์ฐ์ค ํด๋ฆญ์ผ๋ก ์ด๋ฏธ์ง ์คํฌ๋กคํ์ฌ ๋ณผ ์ ์์ต๋๋ค. v2.05.2์์๋ ์ด๋ฏธ์ง ์๋ณธ์ด ์คํฌ๋ฆฐ ํฌ๊ธฐ๋ณด๋ค ํฐ ๊ฒฝ์ฐ ํ๋ฉด์ 0.8 ๋ฐฐ ํฌ๊ธฐ๋ก ์๋์ผ๋ก ์ค์ฌ ๋ณด์ฌ ์ฃผ์ด ๋ ์ข์์ก์ต๋๋ค.
- [์ฃผ์] ๋ ์ผ์ค 4์์๋ vendor/assets/images ๋๋ ํ ๋ฆฌ์ ์์นํ๋ ์ด๋ฏธ์ง ํ์ผ๋ค์ด precompile๋์ง ์๋ ๋ฌธ์ ์ ์ ๋ฐ๊ฒฌํ์ต๋๋ค. ์ด๋ฅผ ์ํ ํด๊ฒฐ์ฑ ์ config/production.rb ํ์ผ์ ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์ถ๊ฐํด ์ค๋๋ค. ์ฆ, precompile ํ ๋ ์ด๋ฏธ์ง ํ์ผ์ ํฌํจํ๋ผ๋ ๋ง์ธ๊ฑฐ์ฃ .
# Precompile vendor/assets/images with Sprockets
config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
- likeable์ด๋ผ๋ polymorphic association์ ์ด์ฉํ์ฌ ์ข์์ ๊ธฐ๋ฅ์ ๊ตฌํํ์์ต๋๋ค. ์ด๋ฅผ ์ํด์ like.rb ๋ชจ๋ธ ํด๋์ค๋ฅผ ์์ฑํ์๊ณ ์ด๊ฒ์ ๋ชจ๋ธ ์ ๋๋ ์ดํฐ๋ฅผ ์ด์ฉํ์์ต๋๋ค.
$ rails g model Like likeable:references{polymorphic} user:references
invoke active_record
create db/migrate/20130827022256_create_likes.rb
create app/models/like.rb
invoke test_unit
create test/models/like_test.rb
create test/fixtures/likes.yml
์ด Like ๋ชจ๋ธ์ User ๋ชจ๋ธ๊ณผ Item ๋ชจ๋ธ์ ์ฐ๊ฒฐํ๋ Join ๋ชจ๋ธ๋ก์ Like ๋ชจ๋ธ์ ์ด๋ค ๋ชจ๋ธ(likeable:polymorphic)๊ณผ๋ belongs_to ๋ก ์ฐ๊ฒฐํ ์ ์๊ฒ ํ์ต๋๋ค.
class Like < ActiveRecord::Base
belongs_to :likeable, polymorphic: true, counter_cache: true
belongs_to :user
end
๋ํ items ๋ฐ์ด๋ธ์๋ likes_count:integer ํ๋๋ฅผ ํ๋ ์ถ๊ฐํฉ๋๋ค. ์ด๊ฒ์ counter_cache์ ์ฌ์ฉํ๊ธฐ ์ํ ํ๋๋ก์ ์ ์ 0์ผ๋ก ์ด๊ธฐํํด ์ค๋๋ค. ์๋๋ ๋ง์ด๊ทธ๋ ์ด์
ํด๋์ค์ ๋ด์ฉ์
๋๋ค.
class AddLikesCountToItems < ActiveRecord::Migration
def change
add_column :items, :likes_count, :integer, default: 0
end
end
User ๋ชจ๋ธ ํด๋์ค์๋ like!, dislike!, liking? ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๊ณ , users_controller.rb ์๋ like, dislike ์ก์
์ ์ถ๊ฐํด ์ฃผ์์ต๋๋ค.
in user.rb
def like!(item)
likes.create!( likeable: item)
end
def dislike!(item)
likes.find_by(likeable: item).destroy!
end
def liking?(item)
if item.nil?
false
else
likes.find_by(likeable: item).present?
end
end
in users_controller.rb
def like
@user = User.find(params[:id])
@item = Item.find(params[:item_id])
@user.like! @item
respond_to do |format|
format.js
end
end
def dislike
@user = User.find(params[:id])
@item = Item.find(params[:item_id])
@user.dislike! @item
respond_to do |format|
format.js
end
end
in routes.rb
get 'users/:id/like/:item_id' => 'users#like', as: :like_item
get 'users/:id/dislike/:item_id' => 'users#dislike', as: :dislike_item
in views/users/like.js.erb
$('#like_<%= @item.id%>').html("<%=j render('items/like_status', item: @item) %>");
in views/users/dislike.js.erb
$('#like_<%= @item.id%>').html("<%=j render('items/like_status', item: @item) %>");
Like์ํ๋ฅผ view ํ ํ๋ฆฟ์ ํ์ํ ๋๋ ์๋์ ๊ฐ์ด erb ์ฝ๋๋ฅผ ์์ฑํ์ต๋๋ค.
<%= item.likers.size %>
<%= link_to_unless( current_user.liking?(item), "Like", like_item_path(current_user.id, item.id), class: 'dislike_state', remote: true) do %>
<%= link_to "Liked", dislike_item_path(current_user.id, item.id), remote: true %>
<% end if user_signed_in? %>
์์ธํ ๊ฒ์ ์์ค๋ฅผ ๋ณด์๊ธฐ ๋ฐ๋๋๋ค.
-
์ด๋ฅผ ์ํ ์ ฌ ์ถ๊ฐ : carrierwave-aws (Gemfile์ ์ถ๊ฐํ๊ณ bundle install)
-
config/initializers/carrierwave.rb ํ์ผ์ ์์ฑํ ํ ์๋์ ๊ฐ์ด carrierwave ์ค์ ์ถ๊ฐํ๊ธฐ (ํ๊ฒฝ๋ณ์๋ aws ๋ณธ์ธ๊ณ์ ์์ ๊ด๋ จ์ ๋ณด๋ฅผ ์ป์ ์ ์์ด)
CarrierWave.configure do |config|
config.storage = :aws
config.aws_bucket = ENV['S3_BUCKET_NAME']
config.aws_acl = :public_read
config.asset_host = 'http://[bucket_name].s3-website-ap-northeast-1.amazonaws.com'
config.aws_authenticated_url_expiration = 60 * 60 * 24 * 365
config.aws_credentials = {
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
}
end
heroku์์ ํ๊ฒฝ๋ณ์๋ฅผ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋ก ์๊ตฐ์. ์ฐธ๊ณ (https://devcenter.heroku.com/articles/s3)
$ heroku config:set AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=yyy
bucket name ํ๊ฒฝ๋ณ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ง์ ํฉ๋๋ค. bucket name ์์ฑํ๋ ๋ฐฉ๋ฒ์ https://console.aws.amazon.com/s3/home ์ฐธ์กฐํ์ธ์. (bucket์ ์๋ง์กด S3 ์ ์ฅ์์)
$ heroku config:set S3_BUCKET_NAME=appname-assets
-
์ฌ์ฉ์ ๊ณ์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝ์ ํ๋กํ ์ฌ์ง์ ์ญ์ ํ ์ ์๋๋ก remove ์ฒดํฌ๋ฐ์ค๋ฅผ ์ถ๊ฐํ์์ต๋๋ค. ๋ณธ์ธ์ ํ๋กํ ์ฌ์ง์ ์ ๋ก๋ํ๋ค๊ฐ ์ญ์ ํ๊ณ ์ถ์ ๋ ์ฒดํฌํ๋ฉด ๋ฉ๋๋ค. ํ๋กํ ์ฌ์ง์ ๋ณ๊ฒฝํ๊ณ ์ ํ ๋๋ ๋ค์ ํ์ผ์ ๋ก๋ํ๋ฉด ๋ฉ๋๋ค.
-
[์ฃผ์์ฌํญ] Devise ์ ฌ์ Rails 4์์ ์ฌ์ฉํ ๋๋ strong parameters์ ๋ํ ๋ถ๋ถ์ ์ถ๊ฐํด ์ฃผ์ด์ผ ํฉ๋๋ค. ์ด์ ๋ํ ์ฐธ๊ณ ๋ https://github.com/plataformatec/devise#strong-parameters ์ ๋๋ค.
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
# Devise with strong parameters
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:avatar, :email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:avatar, :email, :password, :password_confirmation, :current_password, :remove_avatar) }
end
end
2013๋ 8์ 26์ผ(#1), hschoi ๋ธ๋์น์ ์ถ๊ฐ๋ ๋ด์ฉ => ๋ฐฐํฌ ์๋ฌผ๋ ์ด์ ๋ชฉ์ ์ผ๋ก heroku์ ๋ฐฐํฌ
- Gemfile์ ์๋์ ์ ฌ์ ์ถ๊ฐ ํฉ๋๋ค.
group :production do
gem 'pg'
gem 'rails_12factor'
end
๊ทธ๋ฆฌ๊ณ , bundle install ํฉ๋๋ค.
- config/environments/production.rb ํ์ผ์ ์ด์ด ์๋์ ๊ฐ์ด ์์ ํด ์ค๋๋ค.
config.serve_static_assets = true
- ๋ค์์ผ๋ก๋ assets์ ์ฌ์ ์ปดํ์ผ์์ ํฉ๋๋ค.
$ RAILS_ENV=production rake assets:precompile
์ง๊ธ๊น์ง ์์ ํ ๋ด์ฉ์ git ์ ์ฅ์์ commit ํฉ๋๋ค
cd vsns/bigpie๋ก ์ด๋ํ ๋ค์์git init๋ช ๋ น์ผ๋กbigpie๋๋ ํ ๋ฆฌ์ ๋ํgitํ๊ฒฝ์ ์ด๊ธฐํ ์ค๋๋ค.
vsns/bigpie $ git init
vsns/bigpie $ git add .
vsns/bigpie $ git commit -m 'initial commit for heroku deployment'
- CLI์์ ์๋์ ๊ฐ์ ๋ช ๋ น์ ์คํํ์ฌ, heroku์ ์น์๋ฒ์ git ์ ์ฅ์๋ฅผ ๋์์ ์์ฑํฉ๋๋ค.
vsns/bigpie $ heroku create
Creating vast-crag-4195... done, stack is cedar
http://vast-crag-4195.herokuapp.com/ | git@heroku.com:vast-crag-4195.git
- ์ด์ heroku ๋ผ๋ ์๊ฒฉ ์ ์ฅ์๋ฅผ ๋ฑ๋กํด ์ค๋๋ค.
vsns/bigpie $ git remote add heroku git@heroku.com:vast-crag-4195.git
- ๋ค์์ผ๋ก heroku master ๋ธ๋์น๋ก ํ์ฌ์ ๋ก์ปฌ ์ ์ฅ์์ ๋ธ๋์น๋ฅผ git push ํฉ๋๋ค.
vsns/bigpie $ git push heroku master
heroku๋ก ๋ฐฐํฌ๊ฐ ์๋ฃ๋๋ฉด ์๋์ ๊ฐ์ด migration ์์ ์ ํฉ๋๋ค.
vsns/bigpie $ heroku run rake db:schema:dump
์ด์ ๋ธ๋ผ์ฐ์ ์ ์ฃผ์์
๋ ฅ์ฐฝ์ http://vast-crag-4195.herokuapp.com/ ์ ์
๋ ฅํ๊ณ ์ ์ํฉ๋๋ค. ์ง๊ธ๊น์ง์ ์ค์น๊ณผ์ ์์ ๋ณ๋ฌธ์ ๊ฐ ์์๋ค๋ฉด ์นํ์ด์ง๊ฐ ์ ๋๋ก ๋ณด์ฌ์ผ ํฉ๋๋ค.
- My Posts ์์ ฏ์ ์ค๋ฅธ์ชฝ sidebar์ thumbnail ํ์์ผ๋ก ๋ณด์ฌ ์ฃผ๋๋ก ํ์ต๋๋ค.
- My Posts ์์ ฏ์ ๊ฐ thumbnail์ ๋ง์ฐ์ค ์ค๋ฒํ๋ฉฐ box shadow๊ฐ ๋ณด์ด๊ณ ํด๋ฆญํ๋ฉด ํด๋น item ์ ๋ณด๋ก ์ด๋ํฉ๋๋ค.
2013๋ 8์ 25์ผ(#4), hschoi ๋ธ๋์น์ ์ถ๊ฐ๋ ๋ด์ฉ => ๋ณธ์ธ์ ๊ณ์ ์ ๋ณด๋ฅผ ์์ ํ ์ ์๊ฒ ํจ.
- item form์ ์ ๋ก๋๋ photo ๊ฐ ์๋ ๊ฒฝ์ฐ ์ด๋ฏธ์ง๋ฅผ ํ์ํ๊ณ ์ด๋ฏธ์ง๋ฅผ ์ญ์ ํ ์ ์๋ checkbox๋ฅผ ์ถ๊ฐํ์์ต๋๋ค. ์ญ์ checkbox๋ฅผ ์ฒดํฌํ ํ submit ํ๋ฉด ์ด์ ๋ถํฐ๋ ์ด๋ฏธ์ง๊ฐ ์ญ์ ๋ฉ๋๋ค.
- item index ํ์ด์ง์์๋ ๊ฐ ํฌ์คํธ๋ง๋ค tag ๋ฆฌ์คํธ๋ฅผ ๋ณด์ด๊ฒ ํ์ต๋๋ค.
- followers์ followings ๋ฆฌ์คํธ๋ฅผ ๋ณผ ์ ์๋๋ก ๋งํฌ๋ฅผ ์ฐ๊ฒฐํ์์ต๋๋ค.
- navbar์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ์ค๋ฅธ์ชฝ์ผ๋ก ๋ฐฐ์ดํ์ต๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ๋ก๊ทธ์ธ ์ํ์์๋ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ์ด๋ฉ์ผ ์ฃผ์๊ฐ navbar์ ํ์๋๋๋ก ํ์์ต๋๋ค.
- ๋ํ ์ด๋ฉ์ผ ๋ฉ๋ด์ ํ์ ๋ฉ๋ด๋ฅผ ๋์ด ๋ณธ์ธ์ ๊ณ์ ์ ๋ณด๋ฅผ ์์ ํ ์ ์๊ฒ ํ์์ต๋๋ค.
- will_paginate ์ ฌ ์ถ๊ฐํ์์ต๋๋ค.
2013๋ 8์ 25์ผ(#3), hschoi ๋ธ๋์น์ ์ถ๊ฐ๋ ๋ด์ฉ => Summernote ์์ง์ํฌ ์๋ํฐ ์ถ๊ฐํจ.
- item form์ description ๋ด์ฉ์ ์
๋ ฅํ๋ ์ฐฝ์ summernote ์์ง์๊ทธ ์๋ํฐ๋ฅผ ๋ถ์์ต๋๋ค. ์ด๋ฅผ ์ํด
summernote-rails๋ผ๋ ์ ฌ์ ์ถ๊ฐํ์ต๋๋ค. ์ฐธ๊ณ ๋ก ์นด์นด์ค์ ํ์ํ๋์ด ๋ง๋์ summernote ์๋ํฐ๋ฅผ rails์์ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ฐ ๋ง๋ ์ ฌ์ ๋๋ค๋ง, ์ํ์ ์ผ๋ก ์ฌ์ฉํด ๋ดค์ต๋๋ค. ๊ทธ๋ฐ๋ฐ๋ก ๊ด์ฐฎ์ต๋๋ค. - summernote-rails ์ ฌ์ ์ฌ์ฉํ๋ turbolinks์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ํ์ด์ง๊ฐ ๋ณด์ฌ์ง ๋ ์๋ํฐ๊ฐ ๋ณด์ด์ง ์๊ฒ ๋์ด juqery-turbolinks ๋ผ๋ ์ ฌ์ ์ถ๊ฐํ์ฌ ํด๊ฒฐํ์์ต๋๋ค. ์ด์ ๋ํด์๋ Gemfile์ ์ฝ๋ฉํธ ์ถ๊ฐํด ๋์์ต๋๋ค.
- summernote-rails ์ ฌ๊ณผ์ ์ถฉ๋ ๋๋ฌธ์ flatui-rails ์ ฌ์ ์ ๊ฑฐํ์์ต๋๋ค.
- item form์ file upload๋ฅผ ์ด์คํ๋์ด ๋ง๋ค์ด ๋์ผ์ ๊ฒ์ ์ฐธ๊ณ ํ์ฌ simple_form_bootstrap_file_input.rb ํ์ผ์ config/initializers ๋๋ ํ ๋ฆฌ์ ์ถ๊ฐํ์ฌ ์ด์๊ฒ ๋ณด์ด๋๋ก ํ์ต๋๋ค. ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
- item photo ์ ๋ ฅ์ ๋ก์ปฌ๋จธ์ ์ ์์คํ ํ์ผ ๋ฟ๋ง ์๋๋ผ ์ด๋ฏธ์ง URL์ ์ ๋ ฅํด๋ ์ ๋ก๋๊ฐ ๊ฐ๋ฅํ๋๋ก ํ์ต๋๋ค. ์ด๋ฅผ ์ฐํด์ item.rb ๋ชจ๋ธ ํด๋์ค์ attr_accessor :remote_photo_url ์ ์ง์ ํด ์ฃผ์๊ตฌ์. items_controller.rb ํ์ผ์ item_params์ permit ์ต์ ์ :remote_photo_url ํญ๋ชฉ์ ์ถ๊ฐํด ์ฃผ์์ต๋๋ค.
- ๊ทธ๋ฆฌ๊ณ items ํ ์ด๋ธ์ description ํ๋์ ๋ฐ์ดํฐ ์์ฑ์ string ์์ text ๋ก ๋ณ๊ฒฝํ์์ต๋๋ค.
- items ๋ณด์ฌ์ง๋ ์์๋ฅผ updated_at ๋ ์ง๋ฅผ DESC๋ก ์ง์ ํ์ต๋๋ค. ์ด์ ์ต๊ทผ ๊ฐฑ์ ๊ธ์ด ๊ฐ์ฅ ์๋ก ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
- gem 'acts-as-taggable-on'์ ์ฌ์ฉํ์ฌ Tagging ๊ธฐ๋ฅ ์ถ๊ฐ
- Item model์ tagging ๊ด๋ จ ๊ธฐ๋ฅ ์ถ๊ฐ (acts_as_taggable)
- Item ์ ๋ ฅ form์์ tag list ์ ๋ ฅ๋ฐ์ ์ ์๋๋ก ์์
- Item show.html์ tag ๋ณด์ฌ์ฃผ๋๋ก ์์ . tag์ /tags/:tag ๋งํฌ ์ถ๊ฐ
- /tags/:tag ๋ผ์ฐํธ ์ถ๊ฐ
- ItemsController์ index์ tag๋ก ๊ฒ์ํ๋ ๊ธฐ๋ฅ ์ถ๊ฐ
- .gitignore ํ์ผ์ big_pie/public/uploads/* ์ถ๊ฐ
- items ์ปจํธ๋กค๋ฌ์ index์ show ์ก์ ๋ทฐ ํ ํ๋ฆฟ ํ์ผ์ ๋ฆฌํฉํ ๋งํ์์ต๋๋ค. index view ์์๋ _item.html.erb ์ด๋ผ๋ partial template ํ์ผ์ ๋ง๋ค์ด์ ๊ฐ item ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ ์ฃผ๋ ๋ถ๋ถ์ ๋ณ๋์ ํ์ผ๋ก ์ถ์ถํ์์ต๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก index.html.erb ํ์ผ์๋ <%= render @items %> ์ด ํ์ค๋ก ๊ฐ๋จํ๊ฒ ์ ์ฒด item ํฌ์คํธ๋ฅผ ๋ณด์ฌ ์ฃผ๊ฒ ๋ฉ๋๋ค.
- show action view ์์๋ _show_item.html.erb ์ด๋ผ๋ partial template ํ์ผ์ ์๋ก ๋ง๋ค์ด์ ํ๋์ item ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ธ ์ด์๊ฒ ๋ณด์ด๋๋ก ํ์์ต๋๋ค. ๋ฌผ๋ก ์ ์ฒด์ ์ผ๋ก๋ responsive design์ ์ ์งํ๋ฉด์ ๋ง์ด์ฃ . ๋ฐ๋ผ์ show action view template ํ์ผ์๋ <%= render 'show_item', item: @item %>์ ๊ฐ์ด ๊ฐ๋จํ๊ฒ ๋ฆฌํฉํ ๋ง๋๋ ๊ฒ์ด์ฃ .
- _item_html.erb partial template ํ์ผ์์ ์ถ๊ฐํ ๊ธฐ๋ฅ์
actions๋ผ๋ div ํ๊ทธ๋ด์ ์ธ๊ฐ์ง action ๋งํฌ๋ฅผ ์ถ๊ฐํ์์ต๋๋ค:Show,Edit,Destroy. ์ดactionsdiv ํ๊ทธ๋ ๋ํดํธ ์ํ์์๋ display:none ์ผ๋ก ๋์ด ์์ง๋ง, div.hover ์์ display:inline์ผ๋ก ๋ณด์ด๊ฒ ํ์์ต๋๋ค. ๋ฐ๋ผ์ ๋ง์ฐ์ค๋ฅผ ํ๋์ post์ ๊ฐ์ ธ๊ฐ๋ฉด post ์ข์ธกํ๋จ์ ์ธ๊ฐ์ ์ก์ ๋ฒํผ๋ค์ด ๋ณด์ด๊ฒ ๋ฉ๋๋ค.
- ์ ์ฒด ๋ ์ด์์์ ๊ฐ๋ก 2๋จ์ผ๋ก ๊ตฌ์ฑํ์๊ณ , ์ฝ๊ฐ์ ๋ฆฌํฉํ ๋ง์ ์ํํ์์ต๋๋ค.
- file upload์ ํ๊ธํ์ผ๋ช ์ผ ๊ฒฝ์ฐ ํ๊ธ์ด ๋ฐ์ค๋ก ํ์๋๋ ๊ฒ์ ํ๊ธ์ด ์ ๋๋ก ํ์๋๋๋ก ํ์์ต๋๋ค. (config/initializers/carrierwave.rb ํ์ผ์ ์ถ๊ฐํ์ต๋๋ค.)
- devise ์ ฌ์ devise:views ๋ฅผ ์์ฑํ์ต๋๋ค. ๊ทธ๋์ app/views/devise/ ๋๋ ํ ๋ฆฌ์ ๋ทฐํ ํ๋ฆฟ ํ์ผ๋ค์ ๊ฐ์ง๊ณ ์์ ํ ์ ์๊ฒ ๋์์ต๋๋ค.
- ๊ทธ๋ฆฌ๊ณ follow ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด์ users_controller.rb ํ์ผ์ ์ถ๊ฐํ์ต๋๋ค. ์ฌ๊ธฐ์๋ followings, followers, follow, unfollow ์ก์ ์ ์ ์ํ์ต๋๋ค.
- User ๋ชจ๋ธ์๋ avatar ์์ฑ์ ์ถ๊ฐํด์ ๊ฐ์ธ ํ๋กํ ์ฌ์ง์ ์ฌ๋ฆฌ ์ ์๊ฒ ํ์ต๋๋ค. ์ฌ์ง์ด ์๋ ๊ฒฝ์ฐ ๋ํดํธ ์ด๋ฏธ์ง(profile_default.png)๊ฐ ๋ณด์ด๋๋ก ํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ Relationship ์ด๋ผ๋ ๋ชจ๋ธ์ ๋ง๋ค์ด User ๋ชจ๋ธ๊ณผ์ ๊ด๊ณ์ค์ ์ ํ์ต๋๋ค. ๋ํ, User ๋ชจ๋ธ์๋ following?, follow!, unfollow! ๋ฉ์๋๋ฅผ ์ ์ํ์ต๋๋ค.
- relationships_controller.rb ํ์ผ์ ํ์ฌ๋ก์๋ ๋ฑํ ํ์๊ฐ ์์ต๋๋ค.