carte-server 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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