carte-server 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/carte.gemspec +1 -0
- data/lib/carte/client/views/card.cjsx +10 -3
- data/lib/carte/client/views/edit.cjsx +11 -3
- data/lib/carte/client/views/list.cjsx +10 -0
- data/lib/carte/server.rb +14 -4
- data/lib/carte/server/models/card.rb +5 -2
- data/lib/carte/server/version.rb +1 -1
- data/lib/carte/server/views/index.haml +57 -0
- data/package.json +2 -1
- data/spec/server_spec.rb +37 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05fe3f4e42f3d067dbac24787cab7c81c131d10d
|
4
|
+
data.tar.gz: d6d15321dddf1b368e836d3a1a65762c03c7e85f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63aa6e882126fd60d9d878326af349a847219bcfd0dd554dbf80e234648b7df3181b7bf31dc3546879e041f0444cb9b2a274a5730c6cd37318a86ebe3c7bdfb5
|
7
|
+
data.tar.gz: ecd5f98f70dd2f6bb6e39251208abe70b96b39222022dde21754403b257d2f1e1e8230213089c63b30c2bf561bdf663e576ad93973fbbce96637b97e045932c2
|
data/carte.gemspec
CHANGED
@@ -30,7 +30,7 @@ module.exports = React.createClass
|
|
30
30
|
console.log 'Card: render'
|
31
31
|
<div className='col-sm-4 col-xs-12 list-group' style={marginBottom:'0px',padding:"5px"} onMouseOver={@onMouseOver} onMouseLeave={@onMouseLeave}>
|
32
32
|
<div className='list-group-item' style={height:'220px'}>
|
33
|
-
<div>
|
33
|
+
<div style={marginBottom:'10px'}>
|
34
34
|
{
|
35
35
|
if @props.card.get('focused')
|
36
36
|
<i className='glyphicon glyphicon-star' style={marginRight:'5px'} />
|
@@ -51,8 +51,15 @@ module.exports = React.createClass
|
|
51
51
|
</a>
|
52
52
|
</span>
|
53
53
|
</div>
|
54
|
-
<div style={overflow:'hidden',width:'100%',height:'
|
55
|
-
<
|
54
|
+
<div style={overflow:'hidden',width:'100%',height:'75%',wordWrap:'break-word'}>
|
55
|
+
<div dangerouslySetInnerHTML={__html: markdownIt.render @props.card.get('content')} />
|
56
|
+
</div>
|
57
|
+
<div style={{visibility: if @isSafariOrUiWebView || @state.showTools then 'visible' else 'hidden'}}>
|
58
|
+
{
|
59
|
+
if @props.card.get("tags")
|
60
|
+
@props.card.get("tags").map (tag)->
|
61
|
+
<span className="pull-right tools"> <a href={"/#/?tags=" + tag}><i className="glyphicon glyphicon-tag" /> {tag}</a></span>
|
62
|
+
}
|
56
63
|
</div>
|
57
64
|
</div>
|
58
65
|
</div>
|
@@ -1,16 +1,20 @@
|
|
1
1
|
# @cjsx React.DOM
|
2
2
|
$ = require('jquery')
|
3
|
-
React = require('react')
|
3
|
+
React = require('react/addons')
|
4
4
|
Modal = require('react-bootstrap/lib/Modal')
|
5
5
|
Button = require('react-bootstrap/lib/Button')
|
6
|
+
TagsInput = require('react-tagsinput')
|
6
7
|
|
7
8
|
module.exports = React.createClass
|
9
|
+
mixins: [React.addons.LinkedStateMixin]
|
10
|
+
|
8
11
|
displayName: 'Edit'
|
9
12
|
|
10
13
|
getInitialState: ()->
|
11
14
|
updating: false
|
12
15
|
title: @props.card.get('title')
|
13
16
|
content: @props.card.get('content')
|
17
|
+
tags: @props.card.get('tags') || []
|
14
18
|
errors: false
|
15
19
|
|
16
20
|
onChangeTitle: ->
|
@@ -23,9 +27,9 @@ module.exports = React.createClass
|
|
23
27
|
event.preventDefault()
|
24
28
|
@setState updating: true
|
25
29
|
if @props.card.isNew()
|
26
|
-
attributes = {title: @state.title, content: @state.content}
|
30
|
+
attributes = {title: @state.title, content: @state.content, tags: @state.tags}
|
27
31
|
else
|
28
|
-
attributes = {new_title: @state.title, content: @state.content}
|
32
|
+
attributes = {new_title: @state.title, content: @state.content, tags: @state.tags}
|
29
33
|
@props.card.save attributes,
|
30
34
|
success: ()=>
|
31
35
|
@setState updating: false
|
@@ -61,6 +65,10 @@ module.exports = React.createClass
|
|
61
65
|
<label class="control-label">Content</label>
|
62
66
|
<textarea rows="10" className="form-control" value={@state.content} onChange={@onChangeContent} disabled={@state.updating} />
|
63
67
|
</div>
|
68
|
+
<div className="form-group">
|
69
|
+
<label class="control-label">Tags</label>
|
70
|
+
<TagsInput ref='tags' valueLink={this.linkState('tags')} />
|
71
|
+
</div>
|
64
72
|
<div className="form-group" style={{paddingBottom:'17px'}}>
|
65
73
|
<button className="btn btn-default pull-right" onClick={@onClickOk} disabled={@state.updating}>
|
66
74
|
|
@@ -38,6 +38,11 @@ module.exports = React.createClass
|
|
38
38
|
query = $.extend query, {page: page}
|
39
39
|
$.param(query)
|
40
40
|
|
41
|
+
tagParam: (tag)->
|
42
|
+
query = $.extend {}, @props.cards.query
|
43
|
+
query = $.extend query, {tags: tag}
|
44
|
+
$.param(query)
|
45
|
+
|
41
46
|
render: ->
|
42
47
|
console.log 'render', @props.cards.query
|
43
48
|
<div className="container" style={{paddingLeft:"5px",paddingRight:"5px"}}>
|
@@ -48,6 +53,11 @@ module.exports = React.createClass
|
|
48
53
|
<li><a href={"/#/?" + @atozParam()} style={{padding:'6px 12px',fontWeight: if @props.cards.query.sort == 'title' and @props.cards.query.order != 'random' then 'bold' else 'normal'}}>A to Z</a></li>
|
49
54
|
<li><a href={"/#/?" + @latestParam()} style={{padding:'6px 12px',fontWeight: if @props.cards.query.sort == 'updated_at' and @props.cards.query.order != 'random' then 'bold' else 'normal'}}>Latest</a></li>
|
50
55
|
<li><a href={"/#/?" + @randomParam()} style={{padding:'6px 12px',fontWeight: if @props.cards.query.order == 'random' then 'bold' else 'normal'}}>Random</a></li>
|
56
|
+
{
|
57
|
+
if @props.cards.query.tags
|
58
|
+
@props.cards.query.tags.split(',').map (tag)->
|
59
|
+
<li><a href={"/#/?" + @tagParam()} style={padding:'6px 12px'}><i className="glyphicon glyphicon-tag" /> {tag}</a></li>
|
60
|
+
}
|
51
61
|
</ul>
|
52
62
|
</div>
|
53
63
|
<div className="col-sm-6" style={{padding:"0px"}}>
|
data/lib/carte/server.rb
CHANGED
@@ -3,6 +3,7 @@ require 'sinatra/namespace'
|
|
3
3
|
require 'mongoid'
|
4
4
|
require 'mongoid_auto_increment_id'
|
5
5
|
require 'will_paginate_mongoid'
|
6
|
+
require 'mongoid-simple-tags'
|
6
7
|
require 'carte/server/models'
|
7
8
|
|
8
9
|
module Carte
|
@@ -33,6 +34,10 @@ module Carte
|
|
33
34
|
if title = params[:title]
|
34
35
|
cards = cards.any_of({title: /#{title}/})
|
35
36
|
end
|
37
|
+
if params[:tags]
|
38
|
+
tags = params[:tags].split(',')
|
39
|
+
cards = cards.tagged_with_all(tags)
|
40
|
+
end
|
36
41
|
if content = params[:content]
|
37
42
|
cards = cards.any_of({content: /#{content}/})
|
38
43
|
end
|
@@ -60,18 +65,18 @@ module Carte
|
|
60
65
|
current_page = cards.current_page.to_i
|
61
66
|
total_pages = cards.total_pages
|
62
67
|
end
|
63
|
-
cards = cards.map {|card| {id: card.id, title: card.title, content: card.content, version: card.version}}
|
68
|
+
cards = cards.map {|card| {id: card.id, title: card.title, content: card.content, version: card.version, tags: card.tags}}
|
64
69
|
{cards: cards, page: {current: current_page, total: total_pages}}.to_json
|
65
70
|
end
|
66
71
|
|
67
72
|
get '/cards/:title.json' do
|
68
73
|
card = Card.where(title: params[:title]).first
|
69
74
|
halt 404 if card.nil?
|
70
|
-
{card: {id: card.id, title: card.title, content: card.content, version: card.version, lefts: card.lefts(4), rights: card.rights(4)}}.to_json
|
75
|
+
{card: {id: card.id, title: card.title, content: card.content, version: card.version, tags: card.tags, lefts: card.lefts(4), rights: card.rights(4)}}.to_json
|
71
76
|
end
|
72
77
|
|
73
78
|
post '/cards.json' do
|
74
|
-
card = Card.new(json_data)
|
79
|
+
card = Card.new(json_data.slice('title', 'content', 'tags'))
|
75
80
|
if card.save
|
76
81
|
status 201
|
77
82
|
{card: {id: card.id}}.to_json
|
@@ -85,7 +90,8 @@ module Carte
|
|
85
90
|
card = Card.where(title: params[:title]).first
|
86
91
|
halt 404 if card.nil?
|
87
92
|
card.histories.create!
|
88
|
-
|
93
|
+
p json_data.slice('new_title', 'content', 'tags').compact
|
94
|
+
if card.update_attributes(json_data.slice('new_title', 'content', 'tags').compact)
|
89
95
|
status 201
|
90
96
|
{}.to_json
|
91
97
|
else
|
@@ -106,6 +112,10 @@ module Carte
|
|
106
112
|
{history: card.histories}.to_json
|
107
113
|
end
|
108
114
|
|
115
|
+
get '/tags.json' do
|
116
|
+
{tags: Card.all_tags}.to_json
|
117
|
+
end
|
118
|
+
|
109
119
|
error(404) do
|
110
120
|
{}.to_json
|
111
121
|
end
|
@@ -5,6 +5,7 @@ module Carte
|
|
5
5
|
include Mongoid::Document
|
6
6
|
include Mongoid::Timestamps
|
7
7
|
include Mongoid::Attributes::Dynamic
|
8
|
+
include Mongoid::Document::Taggable
|
8
9
|
|
9
10
|
field :title, type: String
|
10
11
|
field :new_title, type: String
|
@@ -29,8 +30,10 @@ module Carte
|
|
29
30
|
end
|
30
31
|
|
31
32
|
before_validation(on: :update) do
|
32
|
-
|
33
|
-
|
33
|
+
if self.new_title
|
34
|
+
self.title = self.new_title
|
35
|
+
self.new_title = nil
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
def self.sample(size=1)
|
data/lib/carte/server/version.rb
CHANGED
@@ -38,5 +38,62 @@
|
|
38
38
|
from { transform: scale(1) rotate(0deg);}
|
39
39
|
to { transform: scale(1) rotate(360deg);}
|
40
40
|
}
|
41
|
+
|
42
|
+
/* https://github.com/olahol/react-tagsinput/blob/master/react-tagsinput.css */
|
43
|
+
.react-tagsinput {
|
44
|
+
border: 1px solid #ccc;
|
45
|
+
background: #fff;
|
46
|
+
padding: 10px;
|
47
|
+
overflow-y: auto;
|
48
|
+
border-radius: 3px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.react-tagsinput-tag {
|
52
|
+
display: block;
|
53
|
+
/*border: 1px solid #a5d24a;*/
|
54
|
+
/*background: #cde69c;*/
|
55
|
+
/*color: #638421;*/
|
56
|
+
/*font-size: 12px;*/
|
57
|
+
/*font-family: 'Helvetica Neue', 'Arial', sans-serif;*/
|
58
|
+
float: left;
|
59
|
+
/*padding: 5px;*/
|
60
|
+
margin-right: 5px;
|
61
|
+
/*margin-bottom: 5px;*/
|
62
|
+
text-decoration: none;
|
63
|
+
border-radius: 2px;
|
64
|
+
}
|
65
|
+
|
66
|
+
.react-tagsinput-invalid {
|
67
|
+
background: #FBD8DB !important;
|
68
|
+
color: #90111A !important;
|
69
|
+
}
|
70
|
+
|
71
|
+
.react-tagsinput-validating {
|
72
|
+
/* background: #FFFACD !important; */
|
73
|
+
}
|
74
|
+
|
75
|
+
.react-tagsinput-remove {
|
76
|
+
font-weight: bold;
|
77
|
+
/* color: #638421; */
|
78
|
+
text-decoration: none;
|
79
|
+
font-size: 11px;
|
80
|
+
cursor: pointer;
|
81
|
+
}
|
82
|
+
|
83
|
+
.react-tagsinput-remove:before {
|
84
|
+
/* content: " x"; */
|
85
|
+
}
|
86
|
+
|
87
|
+
.react-tagsinput-input {
|
88
|
+
background: transparent;
|
89
|
+
/* color: #777; */
|
90
|
+
border: 0;
|
91
|
+
/* font-size: 13px; */
|
92
|
+
/* font-family: 'Helvetica Neue', 'Arial', sans-serif; */
|
93
|
+
/*padding: 5px;*/
|
94
|
+
margin: 0;
|
95
|
+
width: 80px;
|
96
|
+
outline: none;
|
97
|
+
}
|
41
98
|
%body
|
42
99
|
#app
|
data/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "carte-client",
|
3
3
|
"description": "something like dictionary, wiki, or information card",
|
4
|
-
"version": "0.0.
|
4
|
+
"version": "0.0.6",
|
5
5
|
"main": "lib/carte.coffee",
|
6
6
|
"scripts": {
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1"
|
@@ -34,6 +34,7 @@
|
|
34
34
|
"markdown-it": "^4.2.0",
|
35
35
|
"react": "^0.13.2",
|
36
36
|
"react-bootstrap": "^0.21.0",
|
37
|
+
"react-tagsinput": "^1.3.2",
|
37
38
|
"vinyl-source-stream": "^1.1.0",
|
38
39
|
"watchify": "^3.1.2"
|
39
40
|
}
|
data/spec/server_spec.rb
CHANGED
@@ -171,4 +171,41 @@ describe 'API' do
|
|
171
171
|
end
|
172
172
|
end
|
173
173
|
end
|
174
|
+
|
175
|
+
context 'Tags' do
|
176
|
+
it 'can create and get a card with tags' do
|
177
|
+
response = client.post('/cards.json', body: {title: 'card1', content: 'content1', tags: %w(tag1 tag2 tag3)}.to_json)
|
178
|
+
expect(response.code).to eq(201)
|
179
|
+
response = client.get("/cards/card1.json")
|
180
|
+
expect(response['card']['tags']).to eq %w(tag1 tag2 tag3)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'can update card with tags' do
|
184
|
+
response = client.post('/cards.json', body: {title: 'card1', content: 'content1', tags: %w(tag1 tag2 tag3)}.to_json)
|
185
|
+
response = client.put("/cards/card1.json", body: {tags: %w(tag4 tag5 tag6)}.to_json)
|
186
|
+
expect(response.code).to eq(201)
|
187
|
+
response = client.get("/cards/card1.json")
|
188
|
+
expect(response['card']['tags']).to eq %w(tag4 tag5 tag6)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'can list cards with tags' do
|
192
|
+
response = client.post('/cards.json', body: {title: 'card1', content: 'content1', tags: %w(tag1 tag2 tag3)}.to_json)
|
193
|
+
response = client.get("/cards.json", query: {name: '^cards1$'})
|
194
|
+
expect(response['cards'].first['tags']).to eq %w(tag1 tag2 tag3)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'can search cards by tags' do
|
198
|
+
response = client.post('/cards.json', body: {title: 'card1', content: 'content1', tags: %w(tag1 tag2 tag3)}.to_json)
|
199
|
+
response = client.get("/cards.json", query: {tags: 'tag1,tag2,tag3'})
|
200
|
+
expect(response['cards'].size).to eq(1)
|
201
|
+
expect(response['cards'].first['tags']).to eq %w(tag1 tag2 tag3)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'can get tags' do
|
205
|
+
response = client.post('/cards.json', body: {title: 'card1', content: 'content1', tags: %w(tag1 tag2 tag3)}.to_json)
|
206
|
+
response = client.post('/cards.json', body: {title: 'card2', content: 'content2', tags: %w(tag4 tag5 tag6)}.to_json)
|
207
|
+
response = client.get("/tags.json")
|
208
|
+
expect(response['tags'].size).to eq(6)
|
209
|
+
end
|
210
|
+
end
|
174
211
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: carte-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tily
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - '>='
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: mongoid-simple-tags
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
181
195
|
description: something like dictionary, wiki, or information card
|
182
196
|
email:
|
183
197
|
- tidnlyam@gmail.com
|