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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0bfef4c6ba86e6cce646264ebf2e52818ec097f
4
- data.tar.gz: 693f0efaf70c3ec9b5e35e5b5c20838f1a51cdb3
3
+ metadata.gz: 05fe3f4e42f3d067dbac24787cab7c81c131d10d
4
+ data.tar.gz: d6d15321dddf1b368e836d3a1a65762c03c7e85f
5
5
  SHA512:
6
- metadata.gz: d679b7217daad508664271b41e9238433cc56d49ee1f335113489c2f8b6afcaffd98be5e38f11fd2c854b066939a847e61880876a201b4d2a9f2f2d27205601f
7
- data.tar.gz: b3c674856434d6a70e069a23c419b4c0a26977647325ca81503e5cd08f2c659ad5babed67de1c89883088a87535b85e5cb8a1a11dcbeb04f4545f12be052307e
6
+ metadata.gz: 63aa6e882126fd60d9d878326af349a847219bcfd0dd554dbf80e234648b7df3181b7bf31dc3546879e041f0444cb9b2a274a5730c6cd37318a86ebe3c7bdfb5
7
+ data.tar.gz: ecd5f98f70dd2f6bb6e39251208abe70b96b39222022dde21754403b257d2f1e1e8230213089c63b30c2bf561bdf663e576ad93973fbbce96637b97e045932c2
data/carte.gemspec CHANGED
@@ -31,4 +31,5 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency "will_paginate_mongoid"
32
32
  spec.add_dependency "activesupport"
33
33
  spec.add_dependency "haml"
34
+ spec.add_dependency "mongoid-simple-tags"
34
35
  end
@@ -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:'80%',wordWrap:'break-word'}>
55
- <p dangerouslySetInnerHTML={__html: markdownIt.render @props.card.get('content')} />
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">&nbsp;&nbsp;<a href={"/#/?tags=" + tag}><i className="glyphicon glyphicon-tag" />&nbsp;{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
  &nbsp;
@@ -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" />&nbsp;{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
- if card.update_attributes(json_data.slice('new_title', 'content').compact)
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
- self.title = self.new_title
33
- self.new_title = nil
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)
@@ -2,6 +2,6 @@ require 'sinatra/base'
2
2
 
3
3
  module Carte
4
4
  class Server < Sinatra::Base
5
- VERSION = "0.0.5"
5
+ VERSION = "0.0.6"
6
6
  end
7
7
  end
@@ -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.5",
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.5
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