Commit 61c45fc3 by tady

Merge pull request #57 from tadyjp/wip/130410_flow_view

[WIP] create controller flow, stock
parents 248183a5 1de4d68a
bundler_args: --without development --path=~/.bundle
bundler_args: "--without development --path=~/.bundle"
language: ruby
rvm:
- 2.0.0
- 2.0.0
env:
matrix:
- DB_TEST_DATABASE=rendezvous_test DB_TEST_USER=travis
......@@ -11,16 +11,17 @@ env:
- AWS_S3_REGION="us-east-1"
- AWS_S3_BUCKET="rendezvous-travis-bundler"
- RAILS_ENV=test
- secure: A72vqUWQiRal8fCMlobXhYrGVDImUXmrCQGuUc382glay7WdJ4ZsIiVvO1gp8mj7X4HUeejmgY4AUVJy6FmCL4LNxDhdRuOirQ+kOcdsuQKDlMPcbGdoYk23ZBlxeq1tqW9Qs8mNjZYHGx3BFf/qtNhgvTLMr+/wQMZi+110e3g=
services:
- mysql
- mysql
before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
- gem install bundler fog
- "sh ./script/travis/bundle_install.sh"
- 'echo ''gem: --no-ri --no-rdoc'' > ~/.gemrc'
- gem install bundler fog
- sh ./script/travis/bundle_install.sh
before_script:
- mysql -e 'CREATE DATABASE rendezvous_test;'
- cp config/database.yml.example config/database.yml
- cp config/settings.yml.example config/settings.yml
- mysql -e 'CREATE DATABASE rendezvous_test;'
- cp config/database.yml.example config/database.yml
- cp config/settings.yml.example config/settings.yml
after_script:
- "ruby script/travis/bundle_cache.rb"
script: "bundle exec rake db:create db:test:load spec"
- ruby script/travis/bundle_cache.rb
script: bundle exec rake db:create db:test:load spec
......@@ -40,7 +40,7 @@ GEM
arel (4.0.2)
ast (1.1.0)
atomic (1.1.15)
aws-sdk (1.36.0)
aws-sdk (1.36.1)
json (~> 1.4)
nokogiri (>= 1.4.4)
uuidtools (~> 2.1)
......@@ -195,7 +195,7 @@ GEM
net-scp (1.1.2)
net-ssh (>= 2.6.5)
net-ssh (2.8.0)
newrelic_rpm (3.7.3.199)
newrelic_rpm (3.7.3.204)
nio4r (1.0.0)
nokogiri (1.6.1)
mini_portile (~> 0.5.0)
......@@ -232,7 +232,7 @@ GEM
websocket-driver (>= 0.2.0)
polyglot (0.3.4)
powerpack (0.0.9)
premailer (1.8.0)
premailer (1.8.1)
css_parser (>= 1.3.5)
htmlentities (>= 4.0.0)
pry (0.9.12.6)
......@@ -289,8 +289,7 @@ GEM
parser (~> 2.1.3)
powerpack (~> 0.0.6)
rainbow (>= 1.99.1, < 3.0)
sass (3.3.0)
rake
sass (3.3.2)
sass-rails (4.0.1)
railties (>= 4.0.0, < 5.0)
sass (>= 3.1.10)
......@@ -307,7 +306,7 @@ GEM
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
slop (3.4.7)
slop (3.5.0)
sprockets (2.11.0)
hike (~> 1.2)
multi_json (~> 1.0)
......
......@@ -59,6 +59,7 @@ And have fun with your team !
# Test
Rendezvous uses travis-ci for test.
......@@ -66,3 +67,4 @@ Rendezvous uses travis-ci for test.
To speet up `bundle install` in travis-ci, Rendezvous use AWS S3 backet for storing gems.
see also: http://randomerrata.com/post/45827813818/travis-s3
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
# Desc
# nn分前をjsで表示する
# `.js-time-ago`クラスのついた要素の`data-time-ago-at`属性を時刻に変換
# Usage:
# <abbr class="js-time-ago" data-time-ago-at="2014-03-12 00:27:57 +0900"></abbr>
# Example:
# <abbr class="js-time-ago" data-time-ago-at="2014-03-12 00:27:57 +0900">a minute ago</abbr>
$ ->
renderTimeAgo = ->
$('.js-time-ago').each ->
$this = $(@)
$this.text moment($this.data('time-ago-at')).fromNow()
setInterval(renderTimeAgo, 1000 * 10)
renderTimeAgo()
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
......@@ -14,3 +14,14 @@
*= require_self
*/
.text-shadow {
color: #999999;
}
a .text-link {
color: #1e0fbe;
}
a:visited .text-link {
color: #609;
}
// Place all the styles related to the flow controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.mod-hover-hidden:hover {
.mod-hover-hidden-item {
display: initial;
}
}
.mod-hover-hidden {
.mod-hover-hidden-item {
display: none;
}
}
// Place all the styles related to the search controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
// Place all the styles related to the stock controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
class FlowController < ApplicationController
before_action :require_login
def show
@posts = Post.order(updated_at: :desc).limit(20).decorate
end
end
class SearchController < ApplicationController
before_action :require_login
def show
if params[:q].present?
scope = Post.search(params[:q])
else
scope = Post.order(updated_at: :desc)
end
@count = scope.count
@posts = scope.limit(10).decorate
end
end
class StockController < ApplicationController
before_action :require_login
def show
end
end
class HomeController < ApplicationController
class WelcomeController < ApplicationController
skip_before_action :require_login
def top
if user_signed_in?
redirect_to posts_path, status: 301
redirect_to flow_path, status: 301
else
render template: 'home/login'
render template: 'welcome/login'
end
end
end
class FlowDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
end
......@@ -5,4 +5,18 @@ class PostDecorator < Draper::Decorator
h.post_path(model)
end
# 読了時間
# 500文字/1分換算
def read_time
_time_min = model.body.length / 500
case _time_min
when 0
'< 1 min.'
when 1..10
"#{_time_min} min."
else
'> 10 min.'
end
end
end
class PostsDecorator < Draper::CollectionDecorator
def related_tags
_tags = self.map do |_post|
_post.tags
end.flatten.uniq
TagDecorator.decorate_collection(_tags)
end
def related_authors
_authors = self.map do |_post|
_post.author
end.flatten.uniq
UserDecorator.decorate_collection(_authors)
end
end
class SearchDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
end
class StockDecorator < Draper::Decorator
delegate_all
# Define presentation-specific methods here. Helpers are accessed through
# `helpers` (aka `h`). You can override attributes, for example:
#
# def created_at
# helpers.content_tag :span, class: 'time' do
# object.created_at.strftime("%a %m/%d/%y")
# end
# end
end
......@@ -23,6 +23,7 @@ class TagDecorator < Draper::Decorator
# tagをtree viewで表示する
def tree_view_node
_html = ''
_html += %Q{
<a href="#{ self.show_path }" data-name="#{model.name}">
#{model.name} <span class="badge">#{model.posts.count}</span>
......
class UserDecorator < Draper::Decorator
delegate_all
end
module SearchHelper
end
module StockHelper
end
......@@ -10,6 +10,13 @@ class Tag < ActiveRecord::Base
default_scope { order(:updated_at => :desc) }
scope :posts_exist, lambda {
select('tags.*, count(posts.id) as posts_count').
joins(:posts).
group('tags.id').
having('posts_count > 0')
}
# 自分のタグに紐づくPostをすべて`other_tag`へ移動する
def move_all_posts_to!(other_tag)
self.posts.each do |_post|
......
......@@ -7,8 +7,12 @@ class User < ActiveRecord::Base
:recoverable, :rememberable, :trackable, :validatable
devise :omniauthable, omniauth_providers: [:google_oauth2]
has_many :posts
has_many :comments
has_many :posts, foreign_key: 'author_id'
has_many :comments, foreign_key: 'author_id'
scope :post_recently, -> {
User.joins(:posts).group('id').order('posts.updated_at desc')
}
# Device
def self.find_for_google_oauth2(access_token, signed_in_resource = nil)
......@@ -56,4 +60,5 @@ class User < ActiveRecord::Base
google_token_expires_at: Time.now + res_json['expires_in'].seconds
)
end
end
/! view:flow/show
.row
h1
| Flow
small - 最近投稿された記事
.col-xs-8 role="navigation"
.list-group
- @posts.each do |_post|
= render partial: 'posts/large_item', locals: { post: _post }
.col-xs-4
/ .panel.panel-default
/ .panel-heading
/ h2.panel-title この1週間で閲覧の多かった投稿(調整中)
/ .panel-body.list-group
/ - Post.limit(5).each do |_post|
/ = render partial: 'posts/small_item', locals: { post: _post }
.panel.panel-default
.panel-heading
h2.panel-title 最近投稿したユーザー(調整中)
.panel-body.list-group
- User.post_recently.limit(10).each_with_index do |author, i|
a.list-group-item.post-list data-author-id=author.id href="#" = author.name
......@@ -11,15 +11,16 @@ html lang="ja"
= csrf_meta_tags
body class="rails-#{params[:controller]}-#{params[:action]}"
= render partial: 'partials/header_notifications'
- if params[:controller] != 'home'
- if params[:controller] != 'welcome'
= render partial: 'partials/app_header'
.container.container-main
= yield
/! /.container
script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"
script src="//cdnjs.cloudflare.com/ajax/libs/underscore.string/2.3.3/underscore.string.min.js"
script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"
script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"
script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"
javascript:
window.RV = window.RV || {};
window.RV.AllTags = JSON.parse('#{raw Tag.all.pluck(:name).to_json}');
......
......@@ -2,8 +2,12 @@ footer
.container
p
a href="https://github.com/tadyjp/rendezvous" Github
| &nbsp;
| &nbsp;|&nbsp;
a href="https://twitter.com/tady_jp" @tady_jp
| &nbsp;|&nbsp;
a data-target="#header-search-description" data-toggle="modal" href="#" Search
| &nbsp;|&nbsp;
a href="https://help.github.com/articles/github-flavored-markdown" target="_blank" Markdown
| &nbsp;
a href="https://travis-ci.org/tadyjp/rendezvous"
img src="https://travis-ci.org/tadyjp/rendezvous.png"/
......@@ -16,3 +20,43 @@ footer
| &nbsp;
a href="https://gemnasium.com/tadyjp/rendezvous"
img src="https://gemnasium.com/tadyjp/rendezvous.png"/
#header-search-description.modal.fade aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1"
.modal-dialog
.modal-content
.modal-header
button.close aria-hidden="true" data-dismiss="modal" type="button" &times;
h4#myModalLabel.modal-title Search command
.modal-body
dl.dl-horizontal
dt Search by title
dd
pre
a href=(posts_path(q: 'title:ruby')) title:ruby
dt Search by id
dd
pre
a href=(posts_path(q: 'id:123')) id:123
dt Search by body
dd
pre
a href=(posts_path(q: 'body:ruby')) body:ruby
dt Search by author
dd
pre
a href=(posts_path(q: '@taro')) @taro
dt Search by tag
dd
pre
a href=(posts_path(q: '#ruby')) #ruby
dt Search by date
dd
pre
a href=(posts_path(q: 'date:2013-12-16')) date:2013-12-16
dt Search by title or body
dd
pre
a href=(posts_path(q: 'ruby')) ruby
.modal-footer
button.btn.btn-default data-dismiss="modal" type="button" Close
nav.navbar.navbar-default.navbar-fixed-top role="navigation"
.container
.navbar-header
button.navbar-toggle data-target="#bs-example-navbar-collapse-1" data-toggle="collapse" type="button"
span.sr-only Toggle navigation
span.icon-bar
span.icon-bar
span.icon-bar
a.navbar-brand href=root_path Rendezvous
#bs-example-navbar-collapse-1.collapse.navbar-collapse
form#app-search-form.navbar-form.navbar-left action=posts_path role="search"
.collapse.navbar-collapse
form#app-search-form.navbar-form.navbar-left action=search_path role="search"
.input-group
input.form-control name="q" placeholder="Search" type="text" value=params[:q] /
span.input-group-btn
button.btn.btn-default data-disable-with="Searching..." type="submit"
span.glyphicon.glyphicon-search
p#header-search-hint.navbar-text
a data-target="#header-search-description" data-toggle="modal" href="#"
| Search
span.glyphicon.glyphicon-question-sign
a href="https://help.github.com/articles/github-flavored-markdown" target="_blank"
| Markdown
span.glyphicon.glyphicon-question-sign
= form_tag(destroy_user_session_path, :method => :delete, class: 'navbar-form navbar-right') do
= submit_tag 'SignOut', class: 'btn btn-default'
.navbar-right
ul.nav.navbar-nav
li class=('active' if current_page?(stock_path))
a href=stock_path title="Stock"
| Stock
li class=('active' if current_page?(flow_path))
a href=flow_path title="Frow"
| Flow
ul.nav.navbar-nav.navbar-right
li
form
a.btn.btn-primary.navbar-btn href=new_post_path
| Post&nbsp;&nbsp;
span.glyphicon.glyphicon-pencil
img height="25" src=current_user.image_url width="25" /
li.dropdown
a.dropdown-toggle data-toggle="dropdown"
= current_user.name
img src=current_user.image_url width="20" height="20"/
b.caret
ul.dropdown-menu
li
a Account (todo)
li
a Settings (todo)
li.divider
li
a href=destroy_user_session_path data-method="delete" rel="nofollow" SignOut
#header-search-description.modal.fade aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1"
.modal-dialog
.modal-content
.modal-header
button.close aria-hidden="true" data-dismiss="modal" type="button" &times;
h4#myModalLabel.modal-title Search command
.modal-body
dl.dl-horizontal
dt Search by title
dd
pre
a href=(posts_path(q: 'title:ruby')) title:ruby
dt Search by id
dd
pre
a href=(posts_path(q: 'id:123')) id:123
dt Search by body
dd
pre
a href=(posts_path(q: 'body:ruby')) body:ruby
dt Search by author
dd
pre
a href=(posts_path(q: '@taro')) @taro
dt Search by tag
dd
pre
a href=(posts_path(q: '#ruby')) #ruby
dt Search by date
dd
pre
a href=(posts_path(q: 'date:2013-12-16')) date:2013-12-16
dt Search by title or body
dd
pre
a href=(posts_path(q: 'ruby')) ruby
.modal-footer
button.btn.btn-default data-dismiss="modal" type="button" Close
- style do
| \#header-search-hint{
| margin-left: 0;
| }
/ locals:
/ post {Post}
a.list-group-item.post-list.mod-hover-hidden data-post-id=post.id href=post_path(post)
.container-fluid
.row
.col-xs-10
h4.text-link #{post.title}
.col-xs-2
small.pull-right
##{post.id}
.row
.col-xs-8
small.text-success
| #{post.author.name} posted&nbsp;
abbr.js-time-ago data-time-ago-at=post.updated_at
|.&nbsp;&nbsp;
- post.tags.each do |tag|
span.label.label-success ##{tag.name}
| &nbsp;
.col-xs-4
small.pull-right
span.glyphicon.glyphicon-time
span.mod-hover-hidden-item
| 読了時間
| &nbsp;#{post.read_time}&nbsp;&nbsp;
span.glyphicon.glyphicon-edit
span.mod-hover-hidden-item
| コメント
| &nbsp;#{post.comments.count}&nbsp;&nbsp;
.row
.col-xs-12
small.text-shadow
= truncate post.body, length: 240
......@@ -5,10 +5,10 @@
ul.list-group
li.list-group-item
- @post.tags.each do |tag|
span.label.label-info
span.label.label-success
a href=tag.decorate.show_path
| ##{tag.name}
span.label.label-success
span.label.label-info
a href=(posts_path(q: "@#{@post.author.name}"))
| @#{@post.author.name}
span.label.label-danger
......
/ locals:
/ post {Post}
a.list-group-item.post-list data-post-id=post.id href="#" = post.title
/! view:search/show
.row
h1
span "#{params[:q]}"
span.small - 検索結果
span.small #{@count}
.col-xs-8 role="navigation"
.list-group
- @posts.each do |_post|
= render partial: 'posts/large_item', locals: { post: _post }
.col-xs-4
.panel.panel-default
.panel-heading
h2.panel-title "#{params[:q]}"に関連するタグ
.panel-body.list-group
- @posts.related_tags.each do |_tag|
a.list-group-item href=search_path(q: "##{_tag.name}") = _tag.name
.panel.panel-default
.panel-heading
h2.panel-title "#{params[:q]}"に関連するユーザー
.panel-body.list-group
- @posts.related_authors.each do |_author|
a.list-group-item href=search_path(q: "@#{_author.name}") = _author.name
/! view:stock/show
.row
h1
| Stock
small - 保存・蓄積された記事
#sidebar.col-xs-6.col-md-4 role="navigation"
#tab-tree.tab-pane
- cache('tag-tree', :expires_in => 1.hour) do
.list-group
= Tag.posts_exist.decorate.tree_view
.col-xs-12.col-sm-6.col-md-8
#list_post
p#posts-placeholder style="color:#aaa;font-size:30px" &lt;-- Select a post...
/! view:home/login
/! view:welcome/login
.login
.container.marketing
.row style=("margin-top: 40px")
......
......@@ -3,7 +3,11 @@ Rendezvous::Application.routes.draw do
post 'apis/markdown_preview'
post 'apis/file_receiver'
root 'home#top', as: 'root'
root 'welcome#top', as: 'root'
get 'stock' => 'stock#show', as: 'stock'
get 'flow' => 'flow#show', as: 'flow'
get 'search' => 'search#show', as: 'search'
get 'posts/:id/fork' => 'posts#fork', as: 'fork_post'
post 'posts/:id/mail' => 'posts#mail', as: 'mail_post'
......
......@@ -3,12 +3,12 @@ Feature: アクセス制限
Scenario: 非ログイン --> TOPページ
When visit '/'
Then response code is 200
Then response includes '<!--view:home/login-->'
Then response includes '<!--view:welcome/login-->'
Scenario: 非ログイン --> postsページ
When visit '/posts'
Then response code is 200
Then response includes '<!--view:home/login-->'
Then response includes '<!--view:welcome/login-->'
# Scenario: 禁止ユーザーログイン --> TOPページ
# Given login via google with 'taro@example.com'
......@@ -19,23 +19,23 @@ Feature: アクセス制限
# Given login via google with 'taro@example.com'
# When visit '/posts'
# Then response code is 200
# Then response includes '<!--view:home/login-->'
# Then response includes '<!--view:welcome/login-->'
Scenario: ログイン --> TOPページ
Given login
When visit '/'
Then response code is 200
Then response includes '<!--view:post/index-->'
Then response includes '<!--view:flow/show-->'
Scenario: ログイン --> postsページ
Scenario: ログイン --> flowページ
Given login
When visit '/posts'
When visit '/flow'
Then response code is 200
Then response includes '<!--view:post/index-->'
Then response includes '<!--view:flow/show-->'
Scenario: ログイン --> ログアウト
Given login
When logout
Then response code is 200
Then response includes '<!--view:home/login-->'
Then response includes '<!--view:welcome/login-->'
require 'spec_helper'
describe FlowController do
describe "GET 'show'" do
login_user
it "returns http success" do
get 'show'
response.should be_success
end
end
end
require 'spec_helper'
describe SearchController do
describe "GET 'show'" do
login_user
it "returns http success" do
get 'show'
response.should be_success
end
end
end
require 'spec_helper'
describe StockController do
describe "GET 'show'" do
login_user
it "returns http success" do
get 'show'
response.should be_success
end
end
end
require 'spec_helper'
describe HomeController do
describe WelcomeController do
describe "GET 'top'" do
it 'should be successful' do
......@@ -21,8 +21,8 @@ describe HomeController do
it 'should be successful' do
get :top
expect(subject).to redirect_to controller: 'posts',
action: 'index'
expect(subject).to redirect_to controller: 'flow',
action: 'show'
end
it 'should find the right user' do
......
require 'spec_helper'
describe FlowDecorator do
end
require 'spec_helper'
describe SearchDecorator do
end
require 'spec_helper'
describe StockDecorator do
end
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the FlowHelper. For example:
#
# describe FlowHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe FlowHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the SearchHelper. For example:
#
# describe SearchHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe SearchHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the StockHelper. For example:
#
# describe StockHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe StockHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
......@@ -94,6 +94,9 @@ RSpec.configure do |config|
DatabaseRewinder.clean
end
config.include Devise::TestHelpers, :type => :controller
config.extend ControllerMacros, :type => :controller
# Capybara.app_host = "http://127.0.0.1/"
register_poltergeist(config)
......
module ControllerMacros
def login_user
before(:each) do
controller.stub(:authenticate_user!).and_return true
@request.env["devise.mapping"] = Devise.mappings[:user]
sign_in FactoryGirl.create(:alice)
end
end
end
RSpec.configure do |config|
config.include Devise::TestHelpers, type: :controller
end
# RSpec.configure do |config|
# config.include Devise::TestHelpers, type: :controller
# end
require 'spec_helper'
describe "flow/show.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
describe "search/show.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
describe "stock/show.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment