zenpush 0.2.0 → 0.3.0
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.
- data/Gemfile.lock +14 -8
- data/History.md +19 -0
- data/README.markdown +20 -9
- data/bin/zenpush +1 -1
- data/bin/zp +1 -1
- data/lib/zenpush/flavors/github.rb +41 -0
- data/lib/zenpush/flavors/standard.rb +13 -0
- data/lib/zenpush/markdown.rb +22 -6
- data/lib/zenpush/runner.rb +19 -18
- data/lib/zenpush/version.rb +1 -1
- data/lib/zenpush/zendesk.rb +73 -30
- data/lib/zenpush.rb +3 -3
- data/zenpush.gemspec +3 -2
- metadata +21 -3
- data/History.txt +0 -13
data/Gemfile.lock
CHANGED
@@ -1,28 +1,34 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
zenpush (0.
|
4
|
+
zenpush (0.2.0)
|
5
5
|
awesome_print (~> 1.0.0)
|
6
6
|
boson (~> 1.0)
|
7
7
|
httparty (~> 0.8.0)
|
8
8
|
json_pure (~> 1.5.1)
|
9
|
+
pygments.rb (~> 0.4.2)
|
9
10
|
redcarpet (~> 2.1.0)
|
10
11
|
|
11
12
|
GEM
|
12
13
|
remote: http://rubygems.org/
|
13
14
|
specs:
|
14
15
|
awesome_print (1.0.2)
|
15
|
-
boson (1.
|
16
|
-
httparty (0.8.
|
17
|
-
multi_json
|
16
|
+
boson (1.2.4)
|
17
|
+
httparty (0.8.3)
|
18
|
+
multi_json (~> 1.0)
|
18
19
|
multi_xml
|
19
|
-
json_pure (1.5.
|
20
|
+
json_pure (1.5.5)
|
20
21
|
spruz (~> 0.2.8)
|
21
|
-
multi_json (1.
|
22
|
-
multi_xml (0.
|
22
|
+
multi_json (1.6.1)
|
23
|
+
multi_xml (0.5.3)
|
24
|
+
posix-spawn (0.3.6)
|
25
|
+
pygments.rb (0.4.2)
|
26
|
+
posix-spawn (~> 0.3.6)
|
27
|
+
yajl-ruby (~> 1.1.0)
|
23
28
|
rake (0.9.2.2)
|
24
|
-
redcarpet (2.1.
|
29
|
+
redcarpet (2.1.1)
|
25
30
|
spruz (0.2.13)
|
31
|
+
yajl-ruby (1.1.0)
|
26
32
|
|
27
33
|
PLATFORMS
|
28
34
|
ruby
|
data/History.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
## 0.3.0
|
2
|
+
|
3
|
+
* [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown) support, with the `-F github` option. ([alexkwolfe](https://github.com/alexkwolfe))
|
4
|
+
* [Zendesk API v2](http://www.zendesk.com/blog/zendesk-api). `entries` become `topics`. ([alexkwolfe](https://github.com/alexkwolfe))
|
5
|
+
* Automatically create category and forum if either does not exist. ([alexkwolfe](https://github.com/alexkwolfe))
|
6
|
+
|
7
|
+
## 0.2.0
|
8
|
+
|
9
|
+
* HTML support: don't convert topics to HTML if they're not `.md` or `.markdown` files. ([nfo](https://github.com/nfo))
|
10
|
+
|
11
|
+
## 0.1.1
|
12
|
+
|
13
|
+
* New option `:filenames_use_dashes_instead_of_spaces`. ([torandu](https://github.com/torandu))
|
14
|
+
|
15
|
+
## 0.1.0
|
16
|
+
|
17
|
+
* Original version. ([nfo](https://github.com/nfo))
|
18
|
+
* List categories, forums, entries. ([nfo](https://github.com/nfo))
|
19
|
+
* Convert entries from Markdown to HTML before pushing them to Zendesk. ([nfo](https://github.com/nfo))
|
data/README.markdown
CHANGED
@@ -44,18 +44,18 @@ Additional configuration (optional):
|
|
44
44
|
|
45
45
|
$ zp forums
|
46
46
|
|
47
|
-
### Listing
|
47
|
+
### Listing topics in a forum
|
48
48
|
|
49
|
-
$ zp
|
49
|
+
$ zp topics -f <forum_id>
|
50
50
|
|
51
|
-
### Creating/updating
|
51
|
+
### Creating/updating a topic
|
52
52
|
|
53
|
-
Keep an organized folder of your categories, forums, and
|
53
|
+
Keep an organized folder of your categories, forums, and topics. Let's say I have the category "Documentation", containing a forum "REST API", and the topics "Introduction" and "Authentication"; you'll want to keep this file structure:
|
54
54
|
|
55
55
|
Documentation/REST API/Introduction.md
|
56
56
|
Documentation/REST API/Authentication.md
|
57
57
|
|
58
|
-
Creating or updating
|
58
|
+
Creating or updating a topic:
|
59
59
|
|
60
60
|
$ zp push -f <path_to_markdown_file>
|
61
61
|
$ zp push -f <path_to_html_file>
|
@@ -66,13 +66,24 @@ Following the previous example, you would type:
|
|
66
66
|
[~/KB/Documentation]$ zp push -f REST API/Authentication.md
|
67
67
|
[~/KB]$ zp push -f REST Documentation/API/Authentication.md
|
68
68
|
|
69
|
-
The gem will automatically discover the category and forum name of a given
|
69
|
+
The gem will automatically discover the category and forum name of a given topic file. It will also convert your Markdown syntax in HTML before sending it to Zendesk.
|
70
70
|
|
71
|
-
### Check if
|
71
|
+
### Check if a topic exists
|
72
72
|
|
73
73
|
$ zp exists? -f <path_to_markdown_file>
|
74
74
|
|
75
|
+
### Markdown Flavors
|
76
|
+
|
77
|
+
ZenPush supports two different flavors of markdown: 'standard' and 'github'. The latter flavor supports Github specific
|
78
|
+
syntax such as fenced code blocks and language specific highlighting.
|
79
|
+
|
80
|
+
Language highlighting alters the HTML inside code blocks to support styling. You will still need to specify the
|
81
|
+
appropriate CSS in your Zendesk account. Highlighting uses Pygments and any of the Pygments styles can be applied.
|
82
|
+
|
83
|
+
$ zp push -f <path_to_markdown_file> -F github
|
84
|
+
|
75
85
|
## Contributors
|
76
86
|
|
77
|
-
*
|
78
|
-
*
|
87
|
+
* [nfo](https://github.com/nfo)
|
88
|
+
* [torandu](https://github.com/torandu)
|
89
|
+
* [alexkwolfe](https://github.com/alexkwolfe)
|
data/bin/zenpush
CHANGED
data/bin/zp
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'redcarpet'
|
3
|
+
require 'pygments'
|
4
|
+
|
5
|
+
module ZenPush
|
6
|
+
module Flavors
|
7
|
+
|
8
|
+
class SyntaxRenderer < Redcarpet::Render::HTML
|
9
|
+
def block_code(code, language)
|
10
|
+
if language && !language.empty?
|
11
|
+
Pygments.highlight(code, :options => {:lexer => language.to_sym, :encoding => 'utf-8'})
|
12
|
+
else
|
13
|
+
"<pre>#{code}</pre>"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Github
|
19
|
+
def self.to_html(text)
|
20
|
+
renderer = SyntaxRenderer.new(optionize [
|
21
|
+
:with_toc_data,
|
22
|
+
:hard_wrap,
|
23
|
+
:xhtml
|
24
|
+
])
|
25
|
+
markdown = Redcarpet::Markdown.new(renderer, optionize([
|
26
|
+
:fenced_code_blocks,
|
27
|
+
:no_intra_emphasis,
|
28
|
+
:tables,
|
29
|
+
:autolink,
|
30
|
+
:strikethrough,
|
31
|
+
:space_after_headers
|
32
|
+
]))
|
33
|
+
markdown.render(text)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.optionize(options)
|
37
|
+
options.each_with_object({}) { |option, memo| memo[option] = true }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/zenpush/markdown.rb
CHANGED
@@ -1,13 +1,29 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require 'redcarpet/compat'
|
3
2
|
|
4
3
|
module ZenPush
|
4
|
+
module Flavors
|
5
|
+
FILES = Dir[File.join(File.dirname(__FILE__), 'flavors', '**', '*.rb')].freeze
|
6
|
+
NAMES = FILES.collect { |f| File.basename(f, '.rb') }.freeze
|
7
|
+
FILES.each do |file|
|
8
|
+
require file
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
5
12
|
class Markdown
|
13
|
+
def self.to_zendesk_html(file, flavor=nil)
|
14
|
+
select_flavor(flavor).to_html(File.read(file))
|
15
|
+
end
|
6
16
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
::
|
17
|
+
def self.select_flavor(flavor)
|
18
|
+
flavor ||= :standard
|
19
|
+
flavor_class_name = flavor.to_s.capitalize.to_sym
|
20
|
+
if ZenPush::Flavors.const_defined?(flavor_class_name)
|
21
|
+
ZenPush::Flavors.const_get(flavor_class_name)
|
22
|
+
else
|
23
|
+
raise "The '#{flavor}' flavor is not supported. Use: #{ZenPush::Flavors::NAMES.join(', ')}."
|
24
|
+
end
|
11
25
|
end
|
12
26
|
end
|
13
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
data/lib/zenpush/runner.rb
CHANGED
@@ -16,40 +16,41 @@ module ZenPush
|
|
16
16
|
end
|
17
17
|
|
18
18
|
option :forum_id, :type => :numeric
|
19
|
-
desc 'List
|
20
|
-
def
|
21
|
-
ap ZenPush.z.
|
19
|
+
desc 'List topics'
|
20
|
+
def topics(options = {})
|
21
|
+
ap ZenPush.z.topics(options[:forum_id])
|
22
22
|
end
|
23
23
|
|
24
|
-
desc 'Does the
|
24
|
+
desc 'Does the topic matching the given file exist?'
|
25
25
|
option :file, :type => :string
|
26
26
|
def exists?(options = {})
|
27
|
-
category_name, forum_name,
|
28
|
-
|
29
|
-
ap !!
|
27
|
+
category_name, forum_name, topic_title = ZenPush.file_to_category_forum_topic(options[:file])
|
28
|
+
topic = ZenPush.z.find_topic(category_name, forum_name, topic_title)
|
29
|
+
ap !!topic
|
30
30
|
end
|
31
31
|
|
32
|
-
desc 'Create or update
|
32
|
+
desc 'Create or update a topic from the given file'
|
33
33
|
option :file, :type => :string
|
34
|
+
option :flavor, :type => :string
|
34
35
|
def push(options = {})
|
35
|
-
category_name, forum_name,
|
36
|
+
category_name, forum_name, topic_title = ZenPush.file_to_category_forum_topic(options[:file])
|
36
37
|
|
37
|
-
|
38
|
+
topic_body =
|
38
39
|
if options[:file].end_with?('.md') || options[:file].end_with?('.markdown')
|
39
|
-
ZenPush::Markdown.to_zendesk_html(options[:file])
|
40
|
+
ZenPush::Markdown.to_zendesk_html(options[:file], options[:flavor])
|
40
41
|
else
|
41
42
|
File.read(options[:file])
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
if
|
46
|
-
# UPDATE THE
|
47
|
-
ap ZenPush.z.
|
45
|
+
topic = ZenPush.z.find_topic(category_name, forum_name, topic_title)
|
46
|
+
if topic
|
47
|
+
# UPDATE THE TOPIC
|
48
|
+
ap ZenPush.z.put_topic(topic['id'], topic_body)
|
48
49
|
else
|
49
|
-
forum = ZenPush.z.
|
50
|
+
forum = ZenPush.z.find_or_create_forum(category_name, forum_name)
|
50
51
|
if forum
|
51
|
-
# CREATE THE
|
52
|
-
ap ZenPush.z.
|
52
|
+
# CREATE THE TOPIC
|
53
|
+
ap ZenPush.z.post_topic(forum['id'], topic_title, topic_body)
|
53
54
|
else
|
54
55
|
ap "Could not find a forum named '#{forum_name}' in the category '#{category_name}'"
|
55
56
|
exit(-1)
|
data/lib/zenpush/version.rb
CHANGED
data/lib/zenpush/zendesk.rb
CHANGED
@@ -33,7 +33,7 @@ module ZenPush
|
|
33
33
|
|
34
34
|
@options = opts
|
35
35
|
|
36
|
-
self.class.base_uri opts[:uri] + '/api/
|
36
|
+
self.class.base_uri opts[:uri] + '/api/v2'
|
37
37
|
self.class.basic_auth opts[:user], opts[:password]
|
38
38
|
end
|
39
39
|
|
@@ -54,7 +54,7 @@ module ZenPush
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def categories(options = {})
|
57
|
-
self.get('/categories.json', options).parsed_response
|
57
|
+
self.get('/categories.json', options).parsed_response['categories']
|
58
58
|
end
|
59
59
|
|
60
60
|
def category(category_id, options = {})
|
@@ -62,7 +62,7 @@ module ZenPush
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def forums(options = {})
|
65
|
-
self.get('/forums.json', options).parsed_response
|
65
|
+
self.get('/forums.json', options).parsed_response['forums']
|
66
66
|
end
|
67
67
|
|
68
68
|
def forum(forum_id, options = {})
|
@@ -70,20 +70,32 @@ module ZenPush
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def users(options = {})
|
73
|
-
self.get('/users.json', options).parsed_response
|
73
|
+
self.get('/users.json', options).parsed_response['users']
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
77
|
-
self.get("/forums/#{forum_id}/
|
76
|
+
def topics(forum_id, options = { })
|
77
|
+
self.get("/forums/#{forum_id}/topics.json", options).parsed_response['topics']
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
81
|
-
self.get("/
|
80
|
+
def topic(topic_id, options = {})
|
81
|
+
self.get("/topics/#{topic_id}.json", options).parsed_response
|
82
82
|
end
|
83
83
|
|
84
84
|
# Find category by name
|
85
85
|
def find_category(category_name, options = {})
|
86
|
-
self.categories
|
86
|
+
categories = self.categories
|
87
|
+
if categories.is_a?(Array)
|
88
|
+
categories.detect { |c| c['name'] == category_name }
|
89
|
+
else
|
90
|
+
raise "Could not retrieve categories: #{categories}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Find category by name, creating it if it doesn't exist
|
95
|
+
def find_or_create_category(category_name, options={ })
|
96
|
+
find_category(category_name, options) || begin
|
97
|
+
post_category(category_name, options={ })
|
98
|
+
end
|
87
99
|
end
|
88
100
|
|
89
101
|
# Find forum by name, knowing the category name
|
@@ -94,32 +106,63 @@ module ZenPush
|
|
94
106
|
end
|
95
107
|
end
|
96
108
|
|
97
|
-
#
|
98
|
-
def
|
109
|
+
# Given a category name, find a forum by name. Create the category and forum either doesn't exist.
|
110
|
+
def find_or_create_forum(category_name, forum_name, options={ })
|
111
|
+
category = self.find_or_create_category(category_name, options)
|
112
|
+
if category
|
113
|
+
self.forums.detect { |f| f['name'] == forum_name } || post_forum(category['id'], forum_name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Find topic by name, knowing the forum name and category name
|
118
|
+
def find_topic(category_name, forum_name, topic_title, options = {})
|
99
119
|
forum = self.find_forum(category_name, forum_name, options)
|
100
120
|
if forum
|
101
|
-
self.
|
121
|
+
self.topics(forum['id'], options).detect {|t| t['title'] == topic_title}
|
102
122
|
end
|
103
123
|
end
|
104
124
|
|
105
|
-
# Create
|
106
|
-
def
|
107
|
-
self.post(
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
)
|
114
|
-
end
|
115
|
-
|
116
|
-
#
|
117
|
-
def
|
118
|
-
self.
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
125
|
+
# Create a category with a given name
|
126
|
+
def post_category(category_name, options={ })
|
127
|
+
self.post('/categories.json',
|
128
|
+
options.merge(
|
129
|
+
:body => { :category => {
|
130
|
+
:name => category_name
|
131
|
+
} }.to_json
|
132
|
+
)
|
133
|
+
)['category']
|
134
|
+
end
|
135
|
+
|
136
|
+
# Create a forum in the given category id
|
137
|
+
def post_forum(category_id, forum_name, options={ })
|
138
|
+
self.post('/forums.json',
|
139
|
+
options.merge(
|
140
|
+
:body => { :forum => {
|
141
|
+
:name => forum_name,
|
142
|
+
:category_id => category_id
|
143
|
+
} }.to_json
|
144
|
+
)
|
145
|
+
)['forum']
|
146
|
+
end
|
147
|
+
|
148
|
+
# Create a topic in the given forum id
|
149
|
+
def post_topic(forum_id, title, body, options = { })
|
150
|
+
self.post("/topics.json",
|
151
|
+
options.merge(
|
152
|
+
:body => { :topic => {
|
153
|
+
:forum_id => forum_id, :title => title, :body => body
|
154
|
+
} }.to_json
|
155
|
+
)
|
156
|
+
)['topic']
|
157
|
+
end
|
158
|
+
|
159
|
+
# Update a topic in the given forum id
|
160
|
+
def put_topic(id, body, options = { })
|
161
|
+
self.put("/topics/#{id}.json",
|
162
|
+
options.merge(
|
163
|
+
:body => { :topic => { :body => body } }.to_json
|
164
|
+
)
|
165
|
+
)['topic']
|
123
166
|
end
|
124
167
|
|
125
168
|
end
|
data/lib/zenpush.rb
CHANGED
@@ -11,7 +11,7 @@ module ZenPush
|
|
11
11
|
@z ||= ZenPush::Zendesk.new
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def file_to_category_forum_topic(file)
|
15
15
|
absolute_path = File.realpath(file)
|
16
16
|
file_extension = File.extname(file)
|
17
17
|
|
@@ -21,10 +21,10 @@ module ZenPush
|
|
21
21
|
parts.each { |el| el.gsub!(/-/, ' ') }
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
topic_name = File.basename(parts[-1], file_extension)
|
25
25
|
forum_name = parts[-2]
|
26
26
|
category_name = parts[-3]
|
27
27
|
|
28
|
-
return category_name, forum_name,
|
28
|
+
return category_name, forum_name, topic_name
|
29
29
|
end
|
30
30
|
end
|
data/zenpush.gemspec
CHANGED
@@ -17,12 +17,13 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
s.rdoc_options = ["--charset=UTF-8"]
|
20
|
-
|
20
|
+
|
21
21
|
s.add_dependency "boson", "~> 1.0" # Command line
|
22
22
|
s.add_dependency "httparty", "~> 0.8.0" # Zendesk API calls
|
23
23
|
s.add_dependency "redcarpet", "~> 2.1.0" # Markdown to HTML
|
24
|
+
s.add_dependency "pygments.rb", "~> 0.4.2" # Code highlighting for Github flavored markdown
|
24
25
|
s.add_dependency "awesome_print", "~> 1.0.0" # Colorized output of Zendesk responses
|
25
26
|
s.add_dependency "json_pure", "~> 1.5.1" # The C-gem "json" will still be used instead if it's installed
|
26
27
|
|
27
28
|
s.add_development_dependency "rake"
|
28
|
-
end
|
29
|
+
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: zenpush
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nicolas Fouché
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -59,6 +59,22 @@ dependencies:
|
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 2.1.0
|
61
61
|
none: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.4.2
|
68
|
+
none: false
|
69
|
+
name: pygments.rb
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.4.2
|
77
|
+
none: false
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
79
|
version_requirements: !ruby/object:Gem::Requirement
|
64
80
|
requirements:
|
@@ -119,13 +135,15 @@ files:
|
|
119
135
|
- .gitignore
|
120
136
|
- Gemfile
|
121
137
|
- Gemfile.lock
|
122
|
-
- History.
|
138
|
+
- History.md
|
123
139
|
- LICENSE
|
124
140
|
- README.markdown
|
125
141
|
- Rakefile
|
126
142
|
- bin/zenpush
|
127
143
|
- bin/zp
|
128
144
|
- lib/zenpush.rb
|
145
|
+
- lib/zenpush/flavors/github.rb
|
146
|
+
- lib/zenpush/flavors/standard.rb
|
129
147
|
- lib/zenpush/markdown.rb
|
130
148
|
- lib/zenpush/runner.rb
|
131
149
|
- lib/zenpush/version.rb
|
data/History.txt
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
== 0.2.0
|
2
|
-
|
3
|
-
* HTML support: don't convert file before pushing it to Zendesk if it's not a .md or .markdown file.
|
4
|
-
|
5
|
-
== 0.1.1
|
6
|
-
|
7
|
-
* New option `:filenames_use_dashes_instead_of_spaces` (@torandu)
|
8
|
-
|
9
|
-
== 0.1.0
|
10
|
-
|
11
|
-
* Original version
|
12
|
-
* List categories, forums, entries
|
13
|
-
* Convert entries from Markdown to HTML push them to Zendesk
|