highrise_assist 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/README.md +113 -0
- data/Rakefile +1 -0
- data/bin/highrise_assist +5 -0
- data/highrise_assist.gemspec +23 -0
- data/lib/highrise_assist/command/base.rb +56 -0
- data/lib/highrise_assist/command/export.rb +231 -0
- data/lib/highrise_assist/command.rb +20 -0
- data/lib/highrise_assist/file_transfer.rb +30 -0
- data/lib/highrise_assist/runner.rb +73 -0
- data/lib/highrise_assist/version.rb +3 -0
- data/lib/highrise_assist.rb +9 -0
- data/lib/highrise_ext.rb +25 -0
- metadata +109 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
# Highrise Assist
|
2
|
+
|
3
|
+
highrise_assist is command line tool for 37signals' highrise.
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
If you are using http://highrisehq.com/, you defiantly can notice that there are some processes in your company, that doesn't feet to the functionality implemented by 37signals' team. Fortunately they have quite strong API, that can be used for custom purposes. In our company we have bunch of such customizations, that we have decided to group in to the gem and deliver to the community. An the first tool is companies and contacts export by tag (read more below). So highrise_assist is a command line set of tools, which let you do custom operations with your data in highrise.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
$ gem install highrise_assist
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Usage: ./bin/highrise_assist COMMAND [options]
|
16
|
+
|
17
|
+
Commands:
|
18
|
+
* export - export next highrise items:
|
19
|
+
* cases
|
20
|
+
* deals
|
21
|
+
* people
|
22
|
+
* companies
|
23
|
+
* emails
|
24
|
+
* notes
|
25
|
+
* comments
|
26
|
+
* attachments
|
27
|
+
|
28
|
+
Common options:
|
29
|
+
--domain DOMAIN highrise subdomain or full domain name
|
30
|
+
--token TOKEN highrise API authentication token
|
31
|
+
|
32
|
+
Export options:
|
33
|
+
--tag TAG filter items with given tag name
|
34
|
+
--directory DIRECTORY working directory
|
35
|
+
--format FORMAT data format (yaml,xml)
|
36
|
+
--skip-attachments don't download attachments
|
37
|
+
--skip-cases don't export cases
|
38
|
+
--skip-deals don't export deals
|
39
|
+
--skip-notes don't export notes
|
40
|
+
--skip-emails don't export emails
|
41
|
+
--skip-comments don't export comments
|
42
|
+
|
43
|
+
Misc options:
|
44
|
+
-h, --help Show this message
|
45
|
+
-v, --version Show version
|
46
|
+
|
47
|
+
## Export Command
|
48
|
+
|
49
|
+
### Description
|
50
|
+
|
51
|
+
If you are the using highrise for sales, human resources or some other activities, one day you will want to export data from the system. Here is the original article http://bit.ly/sTQr1W, which is describing the process. It's quite easy and works fine, but with highrise_assistexport you can do much better. This tool introduce you possibility to export companies and contacts with all the hierarchy of data including emails, notes, comments, cases, deals and even attachments (with web console you just don't have such possibility) in xml or yaml formats. It is possible to mark exact business objects with the tag and then just export them.
|
52
|
+
|
53
|
+
#### The use case 1:
|
54
|
+
|
55
|
+
As user of higrise
|
56
|
+
I want to archive old contacts and companies
|
57
|
+
So that my list will be clean
|
58
|
+
|
59
|
+
Original help request: http://bit.ly/tIDlwk
|
60
|
+
What 37signals' team recommends is: "Have you considered adding a tag to those contacts called "archived"?"
|
61
|
+
With highrise_assistexport you can easily export all the data tagged as "archived", and than just remove data from the system.
|
62
|
+
|
63
|
+
#### The use case2:
|
64
|
+
|
65
|
+
As user of higrise
|
66
|
+
I want to export all my data including attachments
|
67
|
+
So that I can move data to the other management systems
|
68
|
+
|
69
|
+
With highrise_assistexport you can easily do this operation.
|
70
|
+
|
71
|
+
## Keep your higrise always clean!
|
72
|
+
|
73
|
+
### Synopsis
|
74
|
+
|
75
|
+
$ highrise_assist export OPTIONS
|
76
|
+
|
77
|
+
### Example
|
78
|
+
|
79
|
+
$ highrise_assist export \
|
80
|
+
--domain MYSUBDOMAIN \
|
81
|
+
--token 11111111111111111111111111111111 \
|
82
|
+
--directory highrise_data \
|
83
|
+
--tag old-clients \
|
84
|
+
--format xml \
|
85
|
+
--skip-attachments
|
86
|
+
|
87
|
+
Export result:
|
88
|
+
|
89
|
+
$ tree --dirsfirst
|
90
|
+
.
|
91
|
+
├── cases
|
92
|
+
│ ├── case-573785-test-case
|
93
|
+
│ │ ├── attachments
|
94
|
+
│ │ │ └── 22039573-20111111-p7km4bk33ugutcrrb5mxxw7fjj.jpeg
|
95
|
+
│ │ ├── case-573785-test-case.xml
|
96
|
+
│ │ └── note-78169727-test.xml
|
97
|
+
│ └── case-573786-what-is-highrise
|
98
|
+
│ ├── attachments
|
99
|
+
│ └── case-573786-what-is-highrise.xml
|
100
|
+
├── deals
|
101
|
+
│ └── deal-1471928-test-deal
|
102
|
+
│ ├── attachments
|
103
|
+
│ ├── deal-1471928-test-deal.xml
|
104
|
+
│ └── note-78499917.xml
|
105
|
+
└── persons
|
106
|
+
└── person-92632832-test-test
|
107
|
+
├── attachments
|
108
|
+
├── cases
|
109
|
+
│ └── case-573785-test-case -> ../../../cases/case-573785-test-case
|
110
|
+
├── deals
|
111
|
+
│ └── deal-1471928-test-deal -> ../../../deals/deal-1471928-test-deal
|
112
|
+
└── person-92632832-test-test.xml
|
113
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/highrise_assist
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "highrise_assist/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "highrise_assist"
|
7
|
+
s.version = HighriseAssist::VERSION
|
8
|
+
s.authors = ["Andriy Yanko"]
|
9
|
+
s.email = ["andriy.yanko@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/railsware/highrise_assist"
|
11
|
+
s.summary = %q{Assist for 37signals' highrise}
|
12
|
+
s.description = %q{Assist for 37signals' highrise}
|
13
|
+
|
14
|
+
s.rubyforge_project = "highrise_assist"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "highrise", "~>3.0.0"
|
22
|
+
s.add_runtime_dependency "net-http-persistent", "~>2.3"
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module HighriseAssist
|
4
|
+
module Command
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def inherited(klass)
|
8
|
+
Command.names.push klass.name.split("::").last.underscore
|
9
|
+
super(klass)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@options = options.dup
|
15
|
+
authenticate
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
def run
|
21
|
+
raise NotImplementedError, "'run' is not implemented by #{self.class.name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def authenticate
|
27
|
+
require_option!(:domain, :token)
|
28
|
+
|
29
|
+
Highrise::Base.site = "https://" + options[:domain]
|
30
|
+
Highrise::Base.user = options[:token]
|
31
|
+
end
|
32
|
+
|
33
|
+
def require_option!(*names)
|
34
|
+
names.each do |name|
|
35
|
+
options[name].blank? and abort "#{name} option required"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def log(message)
|
40
|
+
puts message
|
41
|
+
end
|
42
|
+
|
43
|
+
def file_transfer
|
44
|
+
@file_transfer ||= FileTransfer.new(options)
|
45
|
+
end
|
46
|
+
|
47
|
+
def download_file(*args)
|
48
|
+
file_transfer.download(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def upload_file(*args)
|
52
|
+
file_transfer.upload(*args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module HighriseAssist
|
2
|
+
module Command
|
3
|
+
class Export < Base
|
4
|
+
|
5
|
+
FORMATS = %w(yaml xml)
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
|
10
|
+
require_option!(:directory)
|
11
|
+
@root_dir = File.expand_path(options[:directory])
|
12
|
+
|
13
|
+
@format = options[:format] || FORMATS.first
|
14
|
+
FORMATS.include?(@format) or abort "Unsupported format: #{@format}"
|
15
|
+
|
16
|
+
@format_method = "to_#{@format}".to_sym
|
17
|
+
|
18
|
+
@tags = []
|
19
|
+
|
20
|
+
@parties = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :root_dir, :format, :format_method
|
24
|
+
|
25
|
+
def run
|
26
|
+
authenticate
|
27
|
+
|
28
|
+
log "* Fetching data ..."
|
29
|
+
|
30
|
+
fetch_tags if options[:tag]
|
31
|
+
|
32
|
+
%w(Person Company).each do |type|
|
33
|
+
collection = fetch_collection(type)
|
34
|
+
store_collection(collection)
|
35
|
+
@parties[type] = collection
|
36
|
+
end
|
37
|
+
|
38
|
+
%w(Kase Deal).each do |type|
|
39
|
+
next if options[:skip_items].include?(type)
|
40
|
+
collection = fetch_collection(type)
|
41
|
+
filter_casedeal_collection!(collection)
|
42
|
+
store_collection(collection)
|
43
|
+
symlink_casedeal_collection(collection)
|
44
|
+
end
|
45
|
+
|
46
|
+
log "Done."
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def fetch_tags
|
52
|
+
log "* Fetching tags ..."
|
53
|
+
@tags = Highrise::Tag.find(:all).select { |t| t.name == options[:tag] }
|
54
|
+
@tags.empty? and abort "Unknown tag #{options[:tag]}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def fetch_collection(type)
|
58
|
+
klass = Highrise.const_get(type)
|
59
|
+
if @tags.empty?
|
60
|
+
collection = klass.find(:all)
|
61
|
+
else
|
62
|
+
collection = []
|
63
|
+
@tags.each do |tag|
|
64
|
+
collection += klass.find(:all, :params => { :tag_id => tag.id } )
|
65
|
+
end
|
66
|
+
collection.uniq!
|
67
|
+
end
|
68
|
+
log ""
|
69
|
+
log "# #{type}: Found #{collection.size} items"
|
70
|
+
collection
|
71
|
+
end
|
72
|
+
|
73
|
+
# Person, Company, Kase, Deal
|
74
|
+
def store_collection(collection)
|
75
|
+
return if collection.empty?
|
76
|
+
base_dir = File.join(root_dir, item_dirname(collection.first))
|
77
|
+
FileUtils.mkdir_p(base_dir)
|
78
|
+
|
79
|
+
size = collection.size
|
80
|
+
collection.each_with_index do |item, index|
|
81
|
+
log ""
|
82
|
+
log("= #{index+1}of#{size}:")
|
83
|
+
|
84
|
+
dir = File.join(base_dir, item_filename(item))
|
85
|
+
FileUtils.mkdir_p(dir)
|
86
|
+
|
87
|
+
store_item(item, dir, " * ")
|
88
|
+
store_item_collection(item, "Note", :notes, dir) unless options[:skip_items].include?("Note")
|
89
|
+
store_item_collection(item, "Email", :emails, dir) unless options[:skip_items].include?("Email")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Note, Email,
|
94
|
+
# Note Attachments, Email Attachments,
|
95
|
+
# Note Comments, Email Comments,
|
96
|
+
# Note Comment Attachments, Email Comment, Attachments
|
97
|
+
def store_item_collection(item, type, collection_name, base_dir)
|
98
|
+
collection = item.send(collection_name)
|
99
|
+
log " * Found #{collection.size} #{collection_name}"
|
100
|
+
|
101
|
+
attachment_dir = File.join(base_dir, "attachments")
|
102
|
+
FileUtils.mkdir_p(attachment_dir)
|
103
|
+
|
104
|
+
collection.each_with_index do |item, index|
|
105
|
+
attachments = item.attributes['attachments'] || []
|
106
|
+
|
107
|
+
unless options[:skip_items].include?("Comment")
|
108
|
+
comments = item.comments
|
109
|
+
item.attributes['comments'] = comments
|
110
|
+
else
|
111
|
+
comments = []
|
112
|
+
end
|
113
|
+
|
114
|
+
log " * Found #{comments.size} comments, #{attachments.size} attachments"
|
115
|
+
store_item(item, base_dir, " * ")
|
116
|
+
|
117
|
+
attachments.each do |attachment|
|
118
|
+
download_attachment(attachment, attachment_dir, " * ")
|
119
|
+
end
|
120
|
+
|
121
|
+
comments.each do |comment|
|
122
|
+
(comment.attributes['attachments'] || []).each do |attachment|
|
123
|
+
download_attachment(attachment, attachment_dir, " * ")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def store_item(item, dir, log_prefix = "")
|
130
|
+
file = "#{item_filename(item)}.#{format}"
|
131
|
+
path = File.join(dir, file)
|
132
|
+
log "#{log_prefix}Store #{file}"
|
133
|
+
File.open(path, "w") { |f| f << item.send(format_method) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def download_attachment(attachment, dir, log_prefix = "")
|
137
|
+
return if options[:skip_attachments]
|
138
|
+
|
139
|
+
file = "#{attachment.attributes['id']}-#{attachment.attributes['name']}"
|
140
|
+
path = File.join(dir, file)
|
141
|
+
|
142
|
+
log "#{log_prefix}Download attachment #{file}"
|
143
|
+
download_file(attachment.attributes['url'], path)
|
144
|
+
end
|
145
|
+
|
146
|
+
def filter_casedeal_collection!(collection)
|
147
|
+
collection.reject! do |casedeal|
|
148
|
+
parties = casedeal_parties(casedeal)
|
149
|
+
not parties.any? { |party| fetched_party?(party) }
|
150
|
+
end
|
151
|
+
|
152
|
+
log "# Filtered to #{collection.size} items"
|
153
|
+
end
|
154
|
+
|
155
|
+
def symlink_casedeal_collection(collection)
|
156
|
+
collection.each do |casedeal|
|
157
|
+
casedeal_parties(casedeal).each do |party|
|
158
|
+
next unless fetched_party?(party)
|
159
|
+
symlink_casedeal_to_party(casedeal, party)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def casedeal_parties(casedeal)
|
165
|
+
parties = []
|
166
|
+
parties += casedeal.parties
|
167
|
+
parties.push(casedeal.party) if casedeal.is_a?(Highrise::Deal)
|
168
|
+
parties.compact!
|
169
|
+
parties.uniq!
|
170
|
+
parties
|
171
|
+
end
|
172
|
+
|
173
|
+
def fetched_party?(party)
|
174
|
+
!!@parties[party.attributes['type']].detect { |p| p.id == party.id }
|
175
|
+
end
|
176
|
+
|
177
|
+
def symlink_casedeal_to_party(casedeal, party)
|
178
|
+
source_name = item_filename(casedeal)
|
179
|
+
destination_name = item_filename(party)
|
180
|
+
source_path = File.join('..', '..', '..', item_dirname(casedeal), source_name)
|
181
|
+
party_dir = File.join(root_dir, item_dirname(party), item_filename(party))
|
182
|
+
destination_dir = File.join(party_dir, item_dirname(casedeal))
|
183
|
+
|
184
|
+
FileUtils.mkdir_p(destination_dir)
|
185
|
+
Dir.chdir(destination_dir) do
|
186
|
+
log " Symlink #{source_name} to #{destination_name}"
|
187
|
+
FileUtils.ln_s(source_path, source_name)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def item_dirname(item)
|
192
|
+
case item
|
193
|
+
when Highrise::Party
|
194
|
+
item_dirname(item.type_object)
|
195
|
+
when Highrise::Person
|
196
|
+
"persons"
|
197
|
+
when Highrise::Company
|
198
|
+
"companies"
|
199
|
+
when Highrise::Kase
|
200
|
+
"cases"
|
201
|
+
when Highrise::Deal
|
202
|
+
"deals"
|
203
|
+
else
|
204
|
+
raise "Unknown #{item.class}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def item_filename(item)
|
209
|
+
case item
|
210
|
+
when Highrise::Party
|
211
|
+
"#{item.attributes['type']}-#{item.id}-#{item.name}"
|
212
|
+
when Highrise::Person
|
213
|
+
"Person-#{item.id}-#{item.name}"
|
214
|
+
when Highrise::Company
|
215
|
+
"Company-#{item.id}-#{item.name}"
|
216
|
+
when Highrise::Kase
|
217
|
+
"Case-#{item.id}-#{item.name}"
|
218
|
+
when Highrise::Deal
|
219
|
+
"Deal-#{item.id}-#{item.name}"
|
220
|
+
when Highrise::Email
|
221
|
+
"Email-#{item.id}-#{item.title}"
|
222
|
+
when Highrise::Note
|
223
|
+
"Note-#{item.id}-#{item.body}"[0,64]
|
224
|
+
else
|
225
|
+
raise "Unknown #{item.class}"
|
226
|
+
end.parameterize
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module HighriseAssist
|
2
|
+
module Command
|
3
|
+
class << self
|
4
|
+
def names
|
5
|
+
@names ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
def defined?(name)
|
9
|
+
names.include?(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(name, options)
|
13
|
+
self.const_get(name.classify).new(options).run
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require "highrise_assist/command/base"
|
20
|
+
require "highrise_assist/command/export"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "net/http/persistent"
|
2
|
+
|
3
|
+
module HighriseAssist
|
4
|
+
class FileTransfer
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
@http = Net::HTTP::Persistent.new('highrise-file-transfer')
|
9
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
10
|
+
@http
|
11
|
+
end
|
12
|
+
|
13
|
+
def download(from, to)
|
14
|
+
uri = URI.parse(from)
|
15
|
+
|
16
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
17
|
+
get_request.basic_auth @options[:token], ''
|
18
|
+
|
19
|
+
@http.request(uri, get_request) do |response|
|
20
|
+
open(to, 'w') do |io|
|
21
|
+
response.read_body { |chunk| io.write chunk }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def upload(from, to)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$KCODE = 'u' if RUBY_VERSION =~ /^1.8/
|
4
|
+
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
module HighriseAssist
|
8
|
+
class Runner
|
9
|
+
def self.run(*args)
|
10
|
+
new(*args).run!
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@argv = argv
|
15
|
+
@options = {}
|
16
|
+
@options[:skip_items] = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def run!
|
20
|
+
parser = OptionParser.new do |o|
|
21
|
+
o.banner = "Usage: #{$0} COMMAND [options]"
|
22
|
+
|
23
|
+
o.separator ""
|
24
|
+
o.separator "Commands:"
|
25
|
+
o.separator " * export - export next highrise items:"
|
26
|
+
%w(cases deals people companies emails notes comments attachments).each do |item|
|
27
|
+
o.separator " * #{item}"
|
28
|
+
end
|
29
|
+
|
30
|
+
o.separator ""
|
31
|
+
o.separator "Common options:"
|
32
|
+
o.on("--domain DOMAIN", "highrise subdomain or full domain name") { |v| set_domain_option(v) }
|
33
|
+
o.on("--token TOKEN", "highrise API authentication token") { |v| @options[:token] = v }
|
34
|
+
o.separator ""
|
35
|
+
|
36
|
+
o.separator "Export options:"
|
37
|
+
o.on("--tag TAG", "filter items with given tag name") { |v| @options[:tag] = v }
|
38
|
+
o.on("--directory DIRECTORY", "working directory") { |v| @options[:directory] = v }
|
39
|
+
o.on("--format FORMAT", "data format (#{Command::Export::FORMATS.join(',')}) ") { |v| @options[:format] = v }
|
40
|
+
o.on("--skip-attachments", "don't download attachments") { @options[:skip_attachments] = true }
|
41
|
+
o.on("--skip-cases", "don't export cases") { @options[:skip_items] << "Kase" }
|
42
|
+
o.on("--skip-deals", "don't export deals") { @options[:skip_items] << "Deal" }
|
43
|
+
o.on("--skip-notes", "don't export notes") { @options[:skip_items] << "Note" }
|
44
|
+
o.on("--skip-emails", "don't export emails") { @options[:skip_items] << "Email" }
|
45
|
+
o.on("--skip-comments", "don't export comments") { @options[:skip_items] << "Comment" }
|
46
|
+
|
47
|
+
o.separator ""
|
48
|
+
o.separator "Misc options:"
|
49
|
+
o.on_tail("-h", "--help", "Show this message") { puts o; exit }
|
50
|
+
o.on_tail('-v', '--version', "Show version") { puts HighriseAssist::VERSION; exit }
|
51
|
+
end
|
52
|
+
|
53
|
+
parser.parse!(@argv)
|
54
|
+
|
55
|
+
command = @argv.first
|
56
|
+
|
57
|
+
Command.defined?(command) or raise OptionParser::ParseError, "Unknown command #{command.inspect}"
|
58
|
+
|
59
|
+
Command.run(command, @options)
|
60
|
+
rescue OptionParser::ParseError => e
|
61
|
+
warn e.message
|
62
|
+
puts parser.help
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def set_domain_option(value)
|
69
|
+
@options[:domain] = value.include?('.') ? value : "#{value}.highrisehq.com"
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/lib/highrise_ext.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "highrise"
|
2
|
+
|
3
|
+
Highrise::Base.format = :xml
|
4
|
+
|
5
|
+
Highrise::Party.class_eval do
|
6
|
+
def notes
|
7
|
+
Highrise::Note.find_all_across_pages(:from => "/#{type_collection_name}/#{id}/notes.xml")
|
8
|
+
end
|
9
|
+
|
10
|
+
def emails
|
11
|
+
Highrise::Email.find_all_across_pages(:from => "/#{type_collection_name}/#{id}/emails.xml")
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
type_object.name
|
16
|
+
end
|
17
|
+
|
18
|
+
def type_object
|
19
|
+
Highrise.const_get(attributes['type']).new(attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_collection_name
|
23
|
+
Highrise.const_get(attributes['type']).collection_name
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: highrise_assist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Andriy Yanko
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-11-17 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: highrise
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 7
|
29
|
+
segments:
|
30
|
+
- 3
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
version: 3.0.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: net-http-persistent
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 5
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 3
|
48
|
+
version: "2.3"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Assist for 37signals' highrise
|
52
|
+
email:
|
53
|
+
- andriy.yanko@gmail.com
|
54
|
+
executables:
|
55
|
+
- highrise_assist
|
56
|
+
extensions: []
|
57
|
+
|
58
|
+
extra_rdoc_files: []
|
59
|
+
|
60
|
+
files:
|
61
|
+
- .gitignore
|
62
|
+
- Gemfile
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- bin/highrise_assist
|
66
|
+
- highrise_assist.gemspec
|
67
|
+
- lib/highrise_assist.rb
|
68
|
+
- lib/highrise_assist/command.rb
|
69
|
+
- lib/highrise_assist/command/base.rb
|
70
|
+
- lib/highrise_assist/command/export.rb
|
71
|
+
- lib/highrise_assist/file_transfer.rb
|
72
|
+
- lib/highrise_assist/runner.rb
|
73
|
+
- lib/highrise_assist/version.rb
|
74
|
+
- lib/highrise_ext.rb
|
75
|
+
homepage: https://github.com/railsware/highrise_assist
|
76
|
+
licenses: []
|
77
|
+
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
requirements: []
|
102
|
+
|
103
|
+
rubyforge_project: highrise_assist
|
104
|
+
rubygems_version: 1.8.6
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Assist for 37signals' highrise
|
108
|
+
test_files: []
|
109
|
+
|