tagalus 0.5.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.
@@ -0,0 +1,7 @@
1
+ === 0.5.0 / 2009-03-26
2
+
3
+ * 1st Release
4
+
5
+ * Full API access
6
+ * Swappable XML parsers
7
+
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/tagalus.rb
6
+ lib/xmlstruct.rb
7
+ test/test_tagalus.rb
@@ -0,0 +1,65 @@
1
+ = tagalus
2
+
3
+ * http://www.carboni.ca/projects/tagalus/
4
+
5
+ == DESCRIPTION:
6
+
7
+ This module encapsulates the API for tagal.us, a site which helps users
8
+ define tags on twitter or other websites. The basic elements are tags,
9
+ definitions, and comments - and these 3 objects can be written and read
10
+ to/from tagal.us using this gem.
11
+
12
+ There's just 6 useful methods - 3 that read, and 3 that write.
13
+
14
+ This module uses the Carboni.ca XML base, which means you can set which
15
+ XML parser it will use - simply use
16
+ Tagalus.parser = :nokogiri # can be :nokogiri, :hpricot, :rexml, or :libxml
17
+ to change the parser.
18
+
19
+ == FEATURES/PROBLEMS:
20
+
21
+ * Full access to the Tagul.us API
22
+ * Swap out XML parsers - nokogiri, hpricot, rexml, and libxml-ruby are supported
23
+
24
+ == SYNOPSIS:
25
+
26
+ Exmaple usage:
27
+ acc = Tagalus::Account.new("1290185015890")
28
+ acc.define("apisandbox") #=> <Definition>
29
+ acc.comments("apisandbox").each do |comm|
30
+ puts comm.text+"\n"
31
+ end
32
+ acc.post_comment "apisandbox", "Testing, 1,2,3!" #=> <Comment>
33
+
34
+ == REQUIREMENTS:
35
+
36
+ * none, though an XML parser other than REXML will make it run quicker
37
+
38
+ == INSTALL:
39
+
40
+ * sudo gem install tagalus
41
+
42
+ == LICENSE:
43
+
44
+ (The MIT License)
45
+
46
+ Copyright (c) 2009 Michael J. Edgar
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of this software and associated documentation files (the
50
+ 'Software'), to deal in the Software without restriction, including
51
+ without limitation the rights to use, copy, modify, merge, publish,
52
+ distribute, sublicense, and/or sell copies of the Software, and to
53
+ permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be
57
+ included in all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
60
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
62
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
63
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
64
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
65
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/tagalus.rb'
6
+
7
+ Hoe.new('tagalus', Tagalus::VERSION) do |p|
8
+ p.rubyforge_name = 'carbonica' # if different than lowercase project name
9
+ p.developer('Michael J. Edgar', 'adgar@carboni.ca')
10
+
11
+ desc 'Post your blog announcement to blogger.'
12
+ task :post_blogger do
13
+ require 'blogger'
14
+ p.with_config do |config, path|
15
+ break unless config['blogs']
16
+ subject, title, body, urls = p.announcement
17
+
18
+ config['blogs'].each do |site|
19
+ next unless site['url'] =~ /www\.blogger\.com/
20
+ acc = Blogger::Account.new(site['user'],site['password'])
21
+ post = Blogger::Post.new(:title => title, :content => body, :categories => p.blog_categories, :formatter => :rdiscount)
22
+ acc.post(site['blog_id'], post)
23
+
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # vim: syntax=Ruby
@@ -0,0 +1,222 @@
1
+ require 'net/http'
2
+ require 'time'
3
+ require File.dirname(__FILE__)+"/xmlstruct.rb"
4
+ # = tagalus
5
+ #
6
+ # This module encapsulates the API for tagal.us, a site which helps users
7
+ # define tags on twitter or other websites. The basic elements are tags,
8
+ # definitions, and comments - and these 3 objects can be written and read
9
+ # to/from tagal.us using this gem.
10
+ #
11
+ # There's just 6 useful methods - 3 that read, and 3 that write.
12
+ #
13
+ # Exmaple usage:
14
+ # acc = tagalus::Account.new("1290185015890")
15
+ # acc.define("apisandbox") #=> <Definition>
16
+ # acc.comments("apisandbox").each do |comm|
17
+ # puts comm.text+"\n"
18
+ # end
19
+ # acc.post_comment "apisandbox", "Testing, 1,2,3!" #=> <Comment>
20
+ #
21
+ # This module uses the Carboni.ca XML base, which means you can set which
22
+ # XML parser it will use - simply use
23
+ # tagalus.parser = :nokogiri # can be :nokogiri, :hpricot, :rexml, or :libxml
24
+ # to change the parser.
25
+ #
26
+ module Tagalus
27
+ VERSION = '0.5.0'
28
+
29
+ API_ROOT = 'api.tagal.us'
30
+
31
+ class TagalusError < StandardError; end
32
+
33
+ # The list of parsers you are allowed to use with the tagalus module.
34
+ AVAILABLE_PARSERS = [:nokogiri, :hpricot, :rexml, :libxml]
35
+ # Sets which XML parser to use. Must be in AVAILABLE_PARSERS
36
+ def self.parser=(val)
37
+ raise UnsupportedParserError.new("An unsupported parser was provided: #{val}") unless AVAILABLE_PARSERS.include? val
38
+ @@parser = val
39
+ case @@parser
40
+ when :nokogiri
41
+ require 'nokogiri'
42
+ when :hpricot
43
+ require 'hpricot'
44
+ when :rexml
45
+ require 'rexml/document'
46
+ when :libxml
47
+ require 'libxml'
48
+ end
49
+ end
50
+ Tagalus.parser = :rexml
51
+ def self.parser
52
+ @@parser
53
+ end
54
+
55
+ # = Account
56
+ #
57
+ # Encapsulates a user's account on tagal.us. Currently the tagal.us API is rather
58
+ # limited, but it is fully supported. Example usage:
59
+ #
60
+ # acc = tagalus::Account.new("1290185015890")
61
+ # acc.define("apisandbox") #=> <Definition>
62
+ # acc.post_comment "apisandbox", "Testing, 1,2,3!" #=> <Comment>
63
+ #
64
+ class Account
65
+
66
+ include CanParse
67
+
68
+ # Creates a new Account object, with API Key +key+. This key is necessary to
69
+ # create tags, definitions, and comments on the server. Read-only operations don't
70
+ # require a key.
71
+ def initialize(key="")
72
+ @api_key = key
73
+ end
74
+
75
+ # Retrieves the most authoritative definition of the tag with name or id +tag+
76
+ def define tag
77
+ path = "/tag/#{tag}/show.xml"
78
+ doc = http_get path
79
+ Definition.new(:xml => xpath(doc,"//definition").first)
80
+ end
81
+
82
+ # Retrieves all the definitions for the tag with name or id +tag+
83
+ def all_definitions tag
84
+ path = "/definition/#{tag}/show.xml"
85
+ doc = http_get path
86
+
87
+ definitions = []
88
+ xpath(doc,"//definition").each do |entry|
89
+ definitions << Definition.new(:xml => entry)
90
+ end
91
+ definitions
92
+ end
93
+
94
+ # Retrieves all the comments for the tag with name or id +tag+
95
+ def comments tag
96
+ path = "/comment/#{tag}/show.xml"
97
+ doc = http_get path
98
+
99
+ comments = []
100
+ xpath(doc, "//comment").each do |entry|
101
+ comments << Comment.new(:xml => entry)
102
+ end
103
+ comments
104
+ end
105
+
106
+ # creates a comment on tagal.us. The tag must have already been created, or an error will be
107
+ # thrown. +comment+ is a string. +tag+ can be either a tagalus::Tag, String, or Fixnum.
108
+ def post_comment tag, comment
109
+ tag_params = (tag.is_a? String) ? { :the_tag => tag } : {:tag_id => (tag.is_a? Tag) ? tag.id : tag }
110
+ tag_params.merge! :the_comment => comment
111
+ path = "/comment/create.xml"
112
+ doc = http_post path, tag_params
113
+ Comment.new(:xml => doc)
114
+ end
115
+
116
+ # defines a tag on tagal.us. The tag must have already been created, or an error will be
117
+ # thrown. +definition+ is a string. +tag+ can be either a tagalus::Tag, String, or Fixnum.
118
+ def post_definition tag, definition
119
+ tag_params = (tag.is_a? String) ? { :the_tag => tag } : {:tag_id => (tag.is_a? Tag) ? tag.id : tag }
120
+ tag_params.merge! :the_definition => definition
121
+ path = "/definition/create.xml"
122
+ doc = http_post path, tag_params
123
+ puts doc.to_s
124
+ Definition.new(:xml => doc)
125
+ end
126
+
127
+ # creates a tag on tagal.us. The tag must not have already been created, or an error will be
128
+ # thrown. +tag+ and +definition+ are both strings.
129
+ def create_tag tag, definition
130
+ tag_params = { :the_tag => tag, :the_definition => definition }
131
+ path = "/tag/create.xml"
132
+ doc = http_post path, tag_params
133
+
134
+ definition = Definition.new(:xml => xpath(doc,"//definition").first)
135
+ result = Tag.new(:xml => doc)
136
+ result.definitions = [definition]
137
+ result
138
+ end
139
+
140
+ # Helper method for retrieving URLs via GET while escaping parameters and including API-specific
141
+ # parameters
142
+ def http_get(path, query_params = {})
143
+ query_params.merge!(:api_key => @api_key, :api_version => "0001")
144
+ http = Net::HTTP.new API_ROOT
145
+ path = path + "?" + URI.escape(query_params.map {|k,v| "#{k}=#{v}"}.join("&"), /[^-_!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]/n)
146
+ resp = http.get(path)
147
+ raise tagalus::tagalusError.new("Error while querying the path #{path}") if resp.body =~ /something went wrong/
148
+ xml_doc(resp.body)
149
+ end
150
+
151
+ # Helper method for retrieving URLs via POST while escaping parameters and including API-specific
152
+ # parameters
153
+ def http_post(path, query_params = {})
154
+ query_params.merge!(:api_key => @api_key, :api_version => "0001")
155
+ http = Net::HTTP.new API_ROOT
156
+ data = URI.escape(query_params.map {|k,v| "#{k}=#{v}"}.join("&"), /[^-_!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]/n)
157
+ resp = http.post(path, data)
158
+ xml_doc(resp.body)
159
+ end
160
+ end
161
+
162
+ # = Tag
163
+ # Represents a tag retrieved or posted to tagal.us. Fields available:
164
+ # [:id] - the ID of the definition
165
+ # [:tag] - the name of the tag being defined
166
+ # [:updated_at] - When the comment was last updated
167
+ # [:created_at] - When the comment was initially created
168
+ class Tag < XMLStruct
169
+ attr_accessor :definitions
170
+ field :tag, :node, "//the-tag"
171
+ field :id, :node, "//id"
172
+ field :updated_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,"//updated-at").first)) }
173
+ field :created_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,"//created-at").first)) }
174
+ def post_initialize; @id = @id.to_i; end
175
+ end
176
+
177
+ # = Definition
178
+ # Represents a definition retrieved or posted on tagal.us. Fields available:
179
+ # [:id] - the ID of the definition
180
+ # [:meta_info] - no idea what this is, seems to always be empty
181
+ # [:tag_id] - the ID of the tag being defined
182
+ # [:text] - the text of the definition
183
+ # [:user_id] - the ID of the user posting the comment
184
+ # [:updated_at] - When the comment was last updated
185
+ # [:created_at] - When the comment was initially created
186
+ # [:authority] - The number of up-votes minus the number of down-votes.
187
+ class Definition < XMLStruct
188
+ field :text, :node, ".//the-definition"
189
+ field :tag_id, :node, ".//tag-id"
190
+ field :id, :node, ".//id"
191
+ field :authority, :node, ".//authority"
192
+ field :user_id, :node, ".//user-id"
193
+ field :meta_info, :node, ".//meta-info"
194
+ field :updated_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,".//updated-at").first)) }
195
+ field :created_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,".//created-at").first)) }
196
+ def post_initialize
197
+ @tag_id = @tag_id.to_i; @id = @id.to_i; @user_id = @user_id.to_i
198
+ end
199
+ end
200
+
201
+ # = Comment
202
+ # Represents a comment retrieved from tagal.us. Fields available:
203
+ # [:id] - the ID of the comment
204
+ # [:meta_info] - no idea what this is, seems to always be empty
205
+ # [:tag_id] - the ID of the tag
206
+ # [:text] - the text of the comment
207
+ # [:user_id] - the ID of the user posting the comment
208
+ # [:updated_at] - When the comment was last updated
209
+ # [:created_at] - When the comment was initially created
210
+ class Comment < XMLStruct
211
+ field :id, :node, "id"
212
+ field :meta_info, :node, "meta_info"
213
+ field :tag_id, :node, "tag-id"
214
+ field :text, :node, "the-comment"
215
+ field :user_id, :node, "user-id"
216
+ field :updated_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,"//updated-at").first)) }
217
+ field :created_at, :proc, lambda { |entry| Time.xmlschema(xml_content(xpath(entry,"//created-at").first)) }
218
+ def post_initialize #:nodoc:
219
+ @tag_id = @tag_id.to_i; @id = @id.to_i; @user_id = @user_id.to_i
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,111 @@
1
+ # Methods for parser-agnostic parsing
2
+ module CanParse
3
+ # Helper method that encapsulates a string into an XML document
4
+ def xml_doc(body)
5
+ case Tagalus.parser
6
+ when :nokogiri
7
+ Nokogiri::XML(body)
8
+ when :hpricot
9
+ Hpricot(body)
10
+ when :rexml
11
+ REXML::Document.new(body)
12
+ when :libxml
13
+ LibXML::XML::Parser.string(body).parse
14
+ end
15
+ end
16
+
17
+ def xpath(element,path)
18
+ case Tagalus.parser
19
+ when :nokogiri
20
+ element.xpath(path)
21
+ when :hpricot
22
+ puts "in hpricot"
23
+ element/path #force the //
24
+ when :rexml
25
+ REXML::XPath.match(element,path)
26
+ when :libxml
27
+ element.find(path)
28
+ end
29
+ end
30
+
31
+ def xml_content(element)
32
+ case Tagalus.parser
33
+ when :nokogiri
34
+ element.content
35
+ when :hpricot
36
+ element.inner_text
37
+ when :rexml
38
+ element.text
39
+ when :libxml
40
+ element.content
41
+ end
42
+ end
43
+
44
+ def xml_attribute(element,attribute)
45
+ case Tagalus.parser
46
+ when :nokogiri
47
+ element[attribute]
48
+ when :hpricot
49
+ element.get_attribute(attribute)
50
+ when :rexml
51
+ element.attributes[attribute]
52
+ when :nokogiri
53
+ element.attributes[attribute]
54
+ end
55
+ end
56
+ end
57
+
58
+ # Generic XML Structure
59
+ class XMLStruct
60
+
61
+ include CanParse
62
+ extend CanParse
63
+
64
+ class << self
65
+
66
+ attr_accessor :fields
67
+
68
+ def field(name, type, getter, path="")
69
+ @fields ||= {}
70
+ if type == :attribute
71
+ @fields[name.to_sym] = {:type => type, :getter => getter, :path => path}
72
+ else
73
+ @fields[name.to_sym] = {:type => type, :getter => getter}
74
+ end
75
+ attr_accessor name
76
+
77
+ end
78
+ end
79
+
80
+ def post_initialize; end
81
+
82
+ def initialize(opts = {})
83
+ if opts[:xml]
84
+ parse_xml opts[:xml]
85
+ else
86
+ opts.each do |key, value|
87
+ instance_variable_set("@#{key}".to_sym, value)
88
+ end
89
+ end
90
+ post_initialize
91
+ self
92
+ end
93
+
94
+ def parse_xml(entry)
95
+ myfields = self.class.fields
96
+ myfields.each do |k,v|
97
+ if v[:getter].is_a?(String) || v[:getter].is_a?(Symbol)
98
+ if v[:type] == :attribute
99
+ instance_variable_set("@#{k}".to_sym, xml_attribute(xpath(entry,v[:path]).first, v[:getter].to_s))
100
+ elsif v[:type] == :node
101
+ node = xpath(entry, v[:getter].to_s).first
102
+ if node
103
+ instance_variable_set "@#{k}".to_sym, xml_content(node)
104
+ end
105
+ end
106
+ elsif v[:getter].is_a?(Proc)
107
+ instance_variable_set "@#{k}".to_sym, v[:getter].call(entry)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "tagalus"
3
+
4
+ class TestTagalus < Test::Unit::TestCase
5
+ def test_sanity
6
+ flunk "write tests or I will kneecap you"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tagalus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael J. Edgar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-27 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.11.0
24
+ version:
25
+ description: "This module encapsulates the API for tagal.us, a site which helps users define tags on twitter or other websites. The basic elements are tags, definitions, and comments - and these 3 objects can be written and read to/from tagal.us using this gem. There's just 6 useful methods - 3 that read, and 3 that write. This module uses the Carboni.ca XML base, which means you can set which XML parser it will use - simply use Tagalus.parser = :nokogiri # can be :nokogiri, :hpricot, :rexml, or :libxml to change the parser."
26
+ email:
27
+ - adgar@carboni.ca
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - README.txt
40
+ - Rakefile
41
+ - lib/tagalus.rb
42
+ - lib/xmlstruct.rb
43
+ - test/test_tagalus.rb
44
+ has_rdoc: true
45
+ homepage: http://www.carboni.ca/projects/tagalus/
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.txt
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project: carbonica
67
+ rubygems_version: 1.3.1
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: This module encapsulates the API for tagal.us, a site which helps users define tags on twitter or other websites
71
+ test_files:
72
+ - test/test_tagalus.rb