wolfram 0.1.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/.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems' unless Object.const_defined?(:Gem)
3
+ require File.dirname(__FILE__) + "/lib/wolfram/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "wolfram"
7
+ s.version = Wolfram::VERSION
8
+ s.authors = ["Gabriel Horner"]
9
+ s.email = "gabriel.horner@gmail.com"
10
+ s.homepage = "http://github.com/cldwalker/wolfram"
11
+ s.summary = "Wolfram V2 API client"
12
+ s.description = "Explore the vast world of computational knowledge available for free via Wolfram's v2 API."
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+ s.rubyforge_project = 'tagaholic'
15
+ s.executables = ['wolfram']
16
+ s.add_dependency 'nokogiri', '>= 1.4.3'
17
+ s.add_development_dependency 'rr'
18
+ s.add_development_dependency 'bacon', '>= 1.1.0'
19
+ s.add_development_dependency 'bacon-rr'
20
+ s.add_development_dependency 'bacon-bits'
21
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} **/deps.rip]) + %w{Rakefile .gemspec}
22
+ s.files += Dir.glob(['test/fixtures/*.xml'])
23
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
24
+ s.license = 'MIT'
25
+ end
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ == 0.1.0
2
+ * Initial release
3
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2011 Gabriel Horner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,46 @@
1
+ == Description
2
+ Explore the vast world of computational knowledge available for free via Wolfram's v2 API.
3
+
4
+ == Install
5
+
6
+ To install this gem:
7
+
8
+ gem install wolfram
9
+
10
+ == Setup
11
+
12
+ You'll need a Wolfram Appid (api key) by {creating an account
13
+ here}[http://developer.wolframalpha.com/portal/apisignup.html] and
14
+ then clicking on the 'Get an APPID' button.
15
+
16
+ Once you have your own appid, set it in your shell, preferably in your ~/.bashrc:
17
+
18
+ export WOLFRAM_APPID='YOURKEY'
19
+
20
+ If you want to explicitly set your appid in a script:
21
+
22
+ Wolfram.appid = "YOURKEY"
23
+
24
+ == Usage
25
+
26
+ Query away on the commandline!
27
+
28
+ # Calculate distance and travel time between places
29
+ $ wolfram from boston to new york
30
+
31
+ # Solve an equation
32
+ $ wolfram x^3 - 4x^2 + 6x - 24 = 0
33
+
34
+ # Find words ending with able
35
+ $ wolfram words ending with able
36
+
37
+ For many more examples, {see here}[http://www.wolframalpha.com/examples/].
38
+
39
+ == Credits
40
+
41
+ {Ian White}[https://github.com/ianwhite] is the original author of this gem.
42
+
43
+ == Todo
44
+ * More tests!
45
+ * A better inspect
46
+ * Handle more api options including format
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rake'
2
+ require 'fileutils'
3
+
4
+ def gemspec
5
+ @gemspec ||= eval(File.read('.gemspec'), binding, '.gemspec')
6
+ end
7
+
8
+ desc "Build the gem"
9
+ task :gem=>:gemspec do
10
+ sh "gem build .gemspec"
11
+ FileUtils.mkdir_p 'pkg'
12
+ FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
13
+ end
14
+
15
+ desc "Install the gem locally"
16
+ task :install => :gem do
17
+ sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
18
+ end
19
+
20
+ desc "Generate the gemspec"
21
+ task :generate do
22
+ puts gemspec.to_ruby
23
+ end
24
+
25
+ desc "Validate the gemspec"
26
+ task :gemspec do
27
+ gemspec.validate
28
+ end
29
+
30
+ desc 'Run tests'
31
+ task :test do |t|
32
+ sh 'bacon -q -Ilib -I. test/*_test.rb'
33
+ end
34
+
35
+ task :default => :test
data/bin/wolfram ADDED
@@ -0,0 +1,4 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'wolfram'
4
+ Wolfram.run
data/deps.rip ADDED
@@ -0,0 +1 @@
1
+ nokogiri >=1.4.3
data/lib/wolfram.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'nokogiri'
2
+ require 'wolfram/xml_container'
3
+ require 'wolfram/util'
4
+ require 'wolfram/query'
5
+ require 'wolfram/result'
6
+ require 'wolfram/pod'
7
+ require 'wolfram/assumption'
8
+ require 'wolfram/version'
9
+
10
+ module Wolfram
11
+ extend self
12
+
13
+ DefaultQueryURI = "http://api.wolframalpha.com/v2/query"
14
+
15
+ attr_accessor :appid, :query_uri
16
+
17
+ def query_uri
18
+ @query_uri ||= DefaultQueryURI
19
+ end
20
+
21
+ def query(input, options = {})
22
+ Query.new(input, options)
23
+ end
24
+
25
+ def fetch(input, options = {})
26
+ query(input, options).fetch
27
+ end
28
+
29
+ def run(argv=ARGV)
30
+ return puts("Usage: wolfram QUERY") if argv.empty?
31
+ puts fetch(argv.join(' ')).inspect
32
+ rescue MissingNodeError
33
+ warn "Wolfram Error: Invalid response - #{$!.message}"
34
+ rescue RuntimeError
35
+ warn "Wolfram Error: #{$!.message}"
36
+ end
37
+
38
+ class MissingNodeError < RuntimeError; end
39
+ end
40
+
41
+ Wolfram.appid = ENV['WOLFRAM_APPID']
@@ -0,0 +1,68 @@
1
+ module Wolfram
2
+ class Assumption
3
+ include XmlContainer
4
+ include Enumerable
5
+ extend Util
6
+
7
+ delegate :[], :each, :to => :values
8
+
9
+ def self.collection(xml, options = {})
10
+ Nokogiri::XML(xml.to_s).search('assumptions').search('assumption').map {|a_xml| new(a_xml, options)}
11
+ end
12
+
13
+ attr_reader :values
14
+ def initialize(xml, options = {})
15
+ @query = options[:query]
16
+ @xml = Nokogiri::XML(xml.to_s).search('assumption').first
17
+ @xml or raise MissingNodeError, "<assumption> node missing from xml: #{xml[0..20]}..."
18
+ extend Util.module_get(Assumption, @xml['type'])
19
+ @values = Value.collection(@xml, options)
20
+ end
21
+
22
+ def name
23
+ xml['type']
24
+ end
25
+
26
+ def to_s
27
+ name + ": " + values.map(&:desc).join(', ')
28
+ end
29
+
30
+ def inspect
31
+ "#<#{to_s}>"
32
+ end
33
+
34
+ class Value
35
+ include XmlContainer
36
+
37
+ def self.collection(xml, options = {})
38
+ Nokogiri::XML(xml.to_s).search('value').map {|v_xml| new(v_xml, options)}
39
+ end
40
+
41
+ def initialize(xml, options = {})
42
+ @query = options[:query]
43
+ @xml = Nokogiri::XML(xml.to_s).search('value').first
44
+ @xml or raise MissingNodeError, "<value> node missing from xml: #{xml[0..20]}..."
45
+ end
46
+
47
+ def inspect
48
+ "#<#{to_s}>"
49
+ end
50
+
51
+ def to_s
52
+ desc
53
+ end
54
+
55
+ def requery
56
+ Query.new(@query.input, @query.options.merge(:assumption => self))
57
+ end
58
+
59
+ def refetch
60
+ requery.fetch
61
+ end
62
+
63
+ def to_query(key)
64
+ Util.to_query(input, key)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,112 @@
1
+ module Wolfram
2
+ class Pod
3
+ include XmlContainer
4
+ include Enumerable
5
+ extend Util
6
+
7
+ delegate :[], :each, :to => :subpods
8
+
9
+ def self.collection(xml, options = {})
10
+ Nokogiri::XML(xml.to_s).search('pod').map {|p_xml| new(p_xml, options)}
11
+ end
12
+
13
+ attr_reader :subpods, :query, :states
14
+ def initialize(xml, options = {})
15
+ @query = options[:query]
16
+ @xml = Nokogiri::XML(xml.to_s).search('pod').first
17
+ @subpods = Subpod.collection(@xml.search('subpod'), options)
18
+ @states = State.collection(@xml.search('states'), options)
19
+ @xml or raise MissingNodeError, "<pod> node missing from xml: #{xml[0..20]}..."
20
+ types.each {|type| extend type}
21
+ end
22
+
23
+ def to_s
24
+ "#{title}: #{structured? ? plaintext : "'#{markup[0..20]}...'"} #{states.join(", ") if states.any?}"
25
+ end
26
+
27
+ def inspect
28
+ "#<#{scanner}: #{to_s}>"
29
+ end
30
+
31
+ def types
32
+ @types ||= scanner.split(',').map {|type| Util.module_get(Result, type)}
33
+ end
34
+
35
+ def plaintext
36
+ (e = subpods.detect(&:plaintext)) && e.plaintext
37
+ end
38
+
39
+ def img
40
+ (e = subpods.detect(&:plaintext)) && e.img
41
+ end
42
+
43
+ def markup
44
+ @markup ||= (xml.search('markup').text || '')
45
+ end
46
+
47
+ def structured?
48
+ subpods.any?
49
+ end
50
+
51
+ class Subpod
52
+ include XmlContainer
53
+
54
+ def self.collection(xml, options = {})
55
+ Nokogiri::XML(xml.to_s).search('subpod').map {|s_xml| new(s_xml, options)}
56
+ end
57
+
58
+ def initialize(xml, options = {})
59
+ @query = options[:query]
60
+ @xml = Nokogiri::Slop(xml.to_s).search('subpod').first
61
+ @xml or raise MissingNodeError, "<subpod> node missing from xml: #{xml[0..20]}..."
62
+ end
63
+
64
+ def plaintext
65
+ (e = xml.plaintext) && e.text
66
+ end
67
+
68
+ def img
69
+ xml.img
70
+ end
71
+ end
72
+
73
+ class State
74
+ attr_reader :name
75
+
76
+ def self.collection(xml, options = {})
77
+ Nokogiri::XML(xml.to_s).search('state').map {|s_xml| new(s_xml['name'], options)}
78
+ end
79
+
80
+ def initialize(name, options = {})
81
+ @query = options[:query]
82
+ @name = name
83
+ end
84
+
85
+ def to_query(key)
86
+ Util.to_query(name, key)
87
+ end
88
+
89
+ def to_s
90
+ "[#{name}...]"
91
+ end
92
+
93
+ def inspect
94
+ "#<State: #{to_s}>"
95
+ end
96
+
97
+ def requery
98
+ if podstate = @query.params[:podstate]
99
+ podstate = State.new("#{podstate.name},#{name}", :query => @query)
100
+ else
101
+ podstate = self
102
+ end
103
+
104
+ Query.new(@query.input, @query.options.merge(:podstate => podstate))
105
+ end
106
+
107
+ def refetch
108
+ requery.fetch
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,44 @@
1
+ require 'open-uri'
2
+
3
+ module Wolfram
4
+ # When given an input, appid and other query params, creates a Result object
5
+ class Query
6
+ def self.fetch(uri)
7
+ open(uri).read
8
+ end
9
+
10
+ attr_accessor :input, :options, :appid, :query_uri
11
+ def initialize(input, options = {})
12
+ @input = input
13
+ @appid = options.delete(:appid) || Wolfram.appid || raise("No APPID set")
14
+ @query_uri = options.delete(:query_uri) || Wolfram.query_uri
15
+ @options = options
16
+ end
17
+
18
+ # explicitly fetch the result
19
+ def fetch
20
+ @result = Result.new(self.class.fetch(uri), :query => self)
21
+ end
22
+
23
+ def result
24
+ @result ||= fetch
25
+ end
26
+
27
+ # the uri that this query will issue a get request to
28
+ def uri
29
+ "#{query_uri}?#{Util.to_param(params)}"
30
+ end
31
+
32
+ def params
33
+ options.merge(:input => input, :appid => appid)
34
+ end
35
+
36
+ def inspect
37
+ out = "q: \"#{input}\""
38
+ out << " #{options[:podstate]}" if options[:podstate]
39
+ out << " (assuming #{options[:assumption]})" if options[:assumption]
40
+ out << ", a: #{result.datatypes}" if @result
41
+ out
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ module Wolfram
2
+ # Result of a Query. Can have multiple Assumptions and Pods.
3
+ class Result
4
+ include XmlContainer
5
+ include Enumerable
6
+ extend Util
7
+
8
+ delegate :[], :each, :to => :pods
9
+ delegate :uri, :to => :query
10
+
11
+ attr_reader :assumptions, :pods, :query
12
+
13
+ def initialize(xml, options = {})
14
+ @query = options[:query]
15
+ @xml = Nokogiri::XML(xml.to_s).search('queryresult').first
16
+ @xml or raise MissingNodeError, "<queryresult> node missing from xml: #{xml[0..20]}..."
17
+ @assumptions = Assumption.collection(@xml, options)
18
+ @pods = Pod.collection(@xml, options)
19
+ types.each {|mod| extend mod}
20
+ end
21
+
22
+ def success?
23
+ success
24
+ end
25
+
26
+ # shortcut to the first assumption
27
+ def assumption
28
+ assumptions[0]
29
+ end
30
+
31
+ def types
32
+ @types ||= xml['datatypes'].split(',').map {|type| Util.module_get(Result, type)}
33
+ end
34
+
35
+ def inspect
36
+ out = "a: #{xml['datatypes']}"
37
+ out << " (assumptions: #{assumptions.map(&:name).join(', ')})" if !Array(assumptions).empty?
38
+ out << pods.map{|pod| "\n - #{pod.to_s.gsub("\n", "\n ")}"}.join
39
+ out
40
+ end
41
+
42
+ def format
43
+ @query && @query.options[:format]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ require 'cgi'
2
+
3
+ module Wolfram
4
+ module Util
5
+
6
+ # From activesupport
7
+ def self.to_query(obj, key)
8
+ "#{CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(obj.to_s)}"
9
+ end
10
+
11
+ # From activesupport
12
+ def self.to_param(obj)
13
+ obj.map {|key, value| to_query(value, key) }.sort * '&'
14
+ end
15
+
16
+ # Finds or creates a module by name for a given module
17
+ def self.module_get(mod, name)
18
+ mod.const_defined?(name) ? mod.const_get(name) :
19
+ mod.const_set(name, Module.new)
20
+ end
21
+
22
+ def delegate(*meths)
23
+ raise ArgumentError unless meths[-1].is_a?(Hash) && meths[-1].key?(:to)
24
+ to_meth = meths.pop[:to]
25
+ meths.each do |meth|
26
+ define_method(meth) do |*args, &block|
27
+ self.send(to_meth).send(meth, *args, &block)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Wolfram
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,30 @@
1
+ module Wolfram
2
+ # include this module to get sensible default methods for xml container classes
3
+ module XmlContainer
4
+ attr_reader :xml
5
+
6
+ def respond_to?(method, include_private = false)
7
+ xml.key?(method.to_s) || super
8
+ end
9
+
10
+ protected
11
+ def xml_value(val)
12
+ case val
13
+ when "true" then true
14
+ when "false" then false
15
+ when "" then nil
16
+ when /^\d+$/ then val.to_i
17
+ when /^\d+\.\d+$/ then val.to_f
18
+ else val
19
+ end
20
+ end
21
+
22
+ def method_missing(method, *args)
23
+ if xml.key?(method.to_s)
24
+ xml_value(xml[method.to_s])
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+ end
data/test/deps.rip ADDED
@@ -0,0 +1,4 @@
1
+ rr >=0
2
+ bacon >=1.1.0
3
+ bacon-rr >=0
4
+ bacon-bits >=0
@@ -0,0 +1,187 @@
1
+ <queryresult success="true" error="false" numpods="13" datatypes="City,MetropolitanArea,UrbanArea,USCounty,USState" timedout="" timing="3.101" parsetiming="0.117" parsetimedout="false" recalculate="" version="2.0">
2
+ <pod title="Input interpretation" scanner="Identity" id="Input" position="100" error="false" numsubpods="1">
3
+ <subpod title="">
4
+ <plaintext>Boston, Massachusetts</plaintext>
5
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP102619e3b0ea35330h1a000028gg82hdh359db20?MSPStoreType=image/gif&amp;s=12" alt="Boston, Massachusetts" title="Boston, Massachusetts" width="147" height="20"/>
6
+ </subpod>
7
+ </pod>
8
+ <pod title="Populations" scanner="Data" id="Population:CityData" position="200" error="false" numsubpods="1">
9
+ <subpod title="">
10
+ <plaintext>city population | 609023 people (country rank: 22nd) (2008 estimate)
11
+ urban area population | 4.032 million people (Boston urban area) (country rank: 7th) (2000 estimate)
12
+ metro area population | 4.589 million people (Boston metro area) (country rank: 10th) (2009 estimate)</plaintext>
13
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP102719e3b0ea35330h1a00001584427c4e8e6cec?MSPStoreType=image/gif&amp;s=12" alt="city population | 609023 people (country rank: 22nd) (2008 estimate) urban area population | 4.032 million people (Boston urban area) (country rank: 7th) (2000 estimate) metro area population | 4.589 million people (Boston metro area) (country rank: 10th) (2009 estimate)" title="city population | 609023 people (country rank: 22nd) (2008 estimate) urban area population | 4.032 million people (Boston urban area) (country rank: 7th) (2000 estimate) metro area population | 4.589 million people (Boston metro area) (country rank: 10th) (2009 estimate)" width="496" height="160"/>
14
+ </subpod>
15
+ <states count="1">
16
+ <state name="Show history" input="Population:CityData__Show history"/>
17
+ </states>
18
+ </pod>
19
+ <pod title="Location" scanner="Data" id="Location:CityData" position="300" error="false" numsubpods="1">
20
+ <subpod title="">
21
+ <plaintext/>
22
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP102819e3b0ea35330h1a000020e9fi69ecabg1i0?MSPStoreType=image/gif&amp;s=12" alt="" title="" width="300" height="161"/>
23
+ </subpod>
24
+ <states count="2">
25
+ <state name="World map" input="Location:CityData__World map"/>
26
+ <state name="Show coordinates" input="Location:CityData__Show coordinates"/>
27
+ </states>
28
+ <infos count="1">
29
+ <info>
30
+ <link url="http://maps.google.com/maps?t=h&amp;ie=UTF8&amp;ll=42.3216,-71.0891&amp;z=12" text="Satellite image"/>
31
+ </info>
32
+ </infos>
33
+ </pod>
34
+ <pod title="Local map" scanner="Data" id="Map:CityData" position="400" error="false" numsubpods="1">
35
+ <subpod title="">
36
+ <plaintext/>
37
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP102919e3b0ea35330h1a000038e0a9d82ceg5fga?MSPStoreType=image/gif&amp;s=12" alt="" title="" width="256" height="278"/>
38
+ </subpod>
39
+ <states count="3">
40
+ <state name="Metric" input="Map:CityData__Metric"/>
41
+ <state name="Larger display" input="Map:CityData__Larger display"/>
42
+ <statelist count="13" value="9 miles across">
43
+ <state name="2 miles across" input="Map:CityData__2 miles across"/>
44
+ <state name="4 miles across" input="Map:CityData__4 miles across"/>
45
+ <state name="9 miles across" input="Map:CityData__9 miles across"/>
46
+ <state name="18 miles across" input="Map:CityData__18 miles across"/>
47
+ <state name="36 miles across" input="Map:CityData__36 miles across"/>
48
+ <state name="72 miles across" input="Map:CityData__72 miles across"/>
49
+ <state name="140 miles across" input="Map:CityData__140 miles across"/>
50
+ <state name="290 miles across" input="Map:CityData__290 miles across"/>
51
+ <state name="570 miles across" input="Map:CityData__570 miles across"/>
52
+ <state name="1100 miles across" input="Map:CityData__1100 miles across"/>
53
+ <state name="2200 miles across" input="Map:CityData__2200 miles across"/>
54
+ <state name="3900 miles across" input="Map:CityData__3900 miles across"/>
55
+ <state name="7200 miles across" input="Map:CityData__7200 miles across"/>
56
+ </statelist>
57
+ </states>
58
+ <infos count="1">
59
+ <info>
60
+ <link url="http://maps.google.com/maps?t=h&amp;ie=UTF8&amp;ll=42.3216,-71.0891&amp;z=12" text="Satellite image"/>
61
+ </info>
62
+ </infos>
63
+ </pod>
64
+ <pod title="Current local time" scanner="Data" id="CurrentTime:CityData" position="500" error="false" numsubpods="1">
65
+ <subpod title="">
66
+ <plaintext>5:32 pm EST | Sunday, January 23, 2011</plaintext>
67
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103019e3b0ea35330h1a000025c691cb4f654g5c?MSPStoreType=image/gif&amp;s=12" alt="5:32 pm EST | Sunday, January 23, 2011" title="5:32 pm EST | Sunday, January 23, 2011" width="270" height="20"/>
68
+ </subpod>
69
+ </pod>
70
+ <pod title="Current weather" scanner="Data" id="WeatherPod:CityData" position="600" error="false" numsubpods="1">
71
+ <subpod title="">
72
+ <plaintext>19 &#xB0;F (wind chill: 7 &#xB0;F) | relative humidity: 45% | wind: 12 mph | partly cloudy</plaintext>
73
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103119e3b0ea35330h1a000037d137ggg45f08b4?MSPStoreType=image/gif&amp;s=12" alt="19 °F (wind chill: 7 °F) | relative humidity: 45% | wind: 12 mph | partly cloudy" title="19 °F (wind chill: 7 °F) | relative humidity: 45% | wind: 12 mph | partly cloudy" width="496" height="42"/>
74
+ </subpod>
75
+ <states count="2">
76
+ <state name="Show history" input="WeatherPod:CityData__Show history"/>
77
+ <state name="Show metric" input="WeatherPod:CityData__Show metric"/>
78
+ </states>
79
+ </pod>
80
+ <pod title="Economic properties" scanner="Data" id="EconomicProperties:CityData" position="700" error="false" numsubpods="1">
81
+ <subpod title="">
82
+ <plaintext>cost of living index | 1.3 &#xD7; national average (Q3 2010)
83
+ median home price | $332900 (Boston metro area) (annual change: -7.81%) (2009)
84
+ unemployment rate | 8% (September 2010)
85
+ total sales tax rate | 6.25%</plaintext>
86
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103219e3b0ea35330h1a00003ih0c624f9fcig5g?MSPStoreType=image/gif&amp;s=12" alt="cost of living index | 1.3 × national average (Q3 2010) median home price | $332900 (Boston metro area) (annual change: -7.81%) (2009) unemployment rate | 8% (September 2010) total sales tax rate | 6.25%" title="cost of living index | 1.3 × national average (Q3 2010) median home price | $332900 (Boston metro area) (annual change: -7.81%) (2009) unemployment rate | 8% (September 2010) total sales tax rate | 6.25%" width="496" height="158"/>
87
+ </subpod>
88
+ </pod>
89
+ <pod title="Other indicators" scanner="Data" id="QualityOfLife:CityData" position="800" error="false" numsubpods="1">
90
+ <subpod title="">
91
+ <plaintext>total rate of violent crime | 2.4 &#xD7; national average (2008 estimate)
92
+ total rate of property crime | 1.2 &#xD7; national average (2008 estimate)
93
+ average daily traffic delay | 7.1 min/day</plaintext>
94
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103319e3b0ea35330h1a00002c43ica63fee46b2?MSPStoreType=image/gif&amp;s=12" alt="total rate of violent crime | 2.4 × national average (2008 estimate) total rate of property crime | 1.2 × national average (2008 estimate) average daily traffic delay | 7.1 min/day" title="total rate of violent crime | 2.4 × national average (2008 estimate) total rate of property crime | 1.2 × national average (2008 estimate) average daily traffic delay | 7.1 min/day" width="474" height="106"/>
95
+ </subpod>
96
+ <infos count="1">
97
+ <info>
98
+ <units count="1">
99
+ <unit short="min/day" long="minutes per day"/>
100
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103419e3b0ea35330h1a00003eheg0g4i784h0a8?MSPStoreType=image/gif&amp;s=12" width="179" height="26"/>
101
+ </units>
102
+ </info>
103
+ </infos>
104
+ </pod>
105
+ <pod title="Geographic properties" scanner="Data" id="GeographicProperties:CityData" position="900" error="false" numsubpods="1">
106
+ <subpod title="">
107
+ <plaintext>elevation | 46 ft
108
+ area | 48.43 mi^2
109
+ population density | 12576 people/mi^2</plaintext>
110
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103519e3b0ea35330h1a00001a2di1ih5177fie0?MSPStoreType=image/gif&amp;s=12" alt="elevation | 46 ft area | 48.43 mi^2 population density | 12576 people/mi^2" title="elevation | 46 ft area | 48.43 mi^2 population density | 12576 people/mi^2" width="286" height="106"/>
111
+ </subpod>
112
+ <states count="1">
113
+ <state name="Show metric" input="GeographicProperties:CityData__Show metric"/>
114
+ </states>
115
+ <infos count="1">
116
+ <info>
117
+ <units count="3">
118
+ <unit short="ft" long="feet"/>
119
+ <unit short="mi^2" long="square miles"/>
120
+ <unit short="people/mi^2" long="people per square mile"/>
121
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103619e3b0ea35330h1a00003d88iibb6i7093e8?MSPStoreType=image/gif&amp;s=12" width="232" height="74"/>
122
+ </units>
123
+ </info>
124
+ </infos>
125
+ </pod>
126
+ <pod title="Nearby cities" scanner="Data" id="CityHierarchyInfo:CityData" position="1000" error="false" numsubpods="1">
127
+ <subpod title="">
128
+ <plaintext>Cambridge, Massachusetts | 4 miles north-northwest | 105596 people
129
+ Quincy, Massachusetts | 6 miles southeast | 92339 people
130
+ Providence, Rhode Island | 38 miles south-southwest | 171557 people
131
+ New York City, New York | 184 miles southwest | 8.364 million people
132
+ (straight-line distances between city centers)</plaintext>
133
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103719e3b0ea35330h1a00000ddigh995a68547a?MSPStoreType=image/gif&amp;s=12" alt="Cambridge, Massachusetts | 4 miles north-northwest | 105596 people Quincy, Massachusetts | 6 miles southeast | 92339 people Providence, Rhode Island | 38 miles south-southwest | 171557 people New York City, New York | 184 miles southwest | 8.364 million people (straight-line distances between city centers)" title="Cambridge, Massachusetts | 4 miles north-northwest | 105596 people Quincy, Massachusetts | 6 miles southeast | 92339 people Providence, Rhode Island | 38 miles south-southwest | 171557 people New York City, New York | 184 miles southwest | 8.364 million people (straight-line distances between city centers)" width="495" height="209"/>
134
+ </subpod>
135
+ <states count="2">
136
+ <state name="Show metric" input="CityHierarchyInfo:CityData__Show metric"/>
137
+ <state name="More" input="CityHierarchyInfo:CityData__More"/>
138
+ </states>
139
+ </pod>
140
+ <pod title="County" scanner="Data" id="CountyInfo:CityData" position="1100" error="false" numsubpods="1">
141
+ <subpod title="">
142
+ <plaintext>Suffolk County, Massachusetts</plaintext>
143
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103819e3b0ea35330h1a000011cc3hc37d56i612?MSPStoreType=image/gif&amp;s=12" alt="Suffolk County, Massachusetts" title="Suffolk County, Massachusetts" width="201" height="20"/>
144
+ </subpod>
145
+ </pod>
146
+ <pod title="Nicknames" scanner="Data" id="BasicInformation:CityData" position="1200" error="false" numsubpods="1">
147
+ <subpod title="">
148
+ <plaintext>Athens of America | Beantown | The Hub | The Cradle of Liberty</plaintext>
149
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP103919e3b0ea35330h1a0000332h1hcg83g4g27a?MSPStoreType=image/gif&amp;s=12" alt="Athens of America | Beantown | The Hub | The Cradle of Liberty" title="Athens of America | Beantown | The Hub | The Cradle of Liberty" width="452" height="20"/>
150
+ </subpod>
151
+ </pod>
152
+ <pod title="Notable people born in Boston" scanner="Data" id="NotablePeople:CityData" position="1300" error="false" numsubpods="1">
153
+ <subpod title="">
154
+ <plaintext>Benjamin Franklin (1706) | Edgar Allan Poe (1809) | Michael Bloomberg (1942) | Robert F. Kennedy (1925) | James Taylor (1948) | Ralph Waldo Emerson (1803) | Donna Summer (1948) | Guru (1961) | Leonard Nimoy (1931) | Uma Thurman (1970) | ...</plaintext>
155
+ <img src="http://www1.wolframalpha.com/Calculate/MSP/MSP104019e3b0ea35330h1a00006509g4f69da8a7i5?MSPStoreType=image/gif&amp;s=12" alt="Benjamin Franklin (1706) | Edgar Allan Poe (1809) | Michael Bloomberg (1942) | Robert F. Kennedy (1925) | James Taylor (1948) | Ralph Waldo Emerson (1803) | Donna Summer (1948) | Guru (1961) | Leonard Nimoy (1931) | Uma Thurman (1970) | ..." title="Benjamin Franklin (1706) | Edgar Allan Poe (1809) | Michael Bloomberg (1942) | Robert F. Kennedy (1925) | James Taylor (1948) | Ralph Waldo Emerson (1803) | Donna Summer (1948) | Guru (1961) | Leonard Nimoy (1931) | Uma Thurman (1970) | ..." width="461" height="101"/>
156
+ </subpod>
157
+ <states count="4">
158
+ <state name="More" input="NotablePeople:CityData__More"/>
159
+ <state name="Show dates" input="NotablePeople:CityData__Show dates"/>
160
+ <state name="Show occupations" input="NotablePeople:CityData__Show occupations"/>
161
+ <state name="Show deaths" input="NotablePeople:CityData__Show deaths"/>
162
+ </states>
163
+ </pod>
164
+ <assumptions count="2">
165
+ <assumption type="Clash" word="boston" count="4">
166
+ <value name="City" desc="a city" input="*C.boston-_*City-"/>
167
+ <value name="Surname" desc="a surname" input="*C.boston-_*Surname-"/>
168
+ <value name="AdministrativeDivision" desc="an administrative division" input="*C.boston-_*AdministrativeDivision-"/>
169
+ <value name="GivenName" desc="a given name" input="*C.boston-_*GivenName-"/>
170
+ </assumption>
171
+ <assumption type="SubCategory" word="boston" count="7">
172
+ <value name="{Boston, Massachusetts, UnitedStates}" desc="Boston (Massachusetts, USA)" input="*DPClash.CityE.boston-_**Boston.Massachusetts.UnitedStates--"/>
173
+ <value name="{Boston, NewYork, UnitedStates}" desc="Boston (New York, USA)" input="*DPClash.CityE.boston-_**Boston.NewYork.UnitedStates--"/>
174
+ <value name="{Boston, Lincolnshire, UnitedKingdom}" desc="Boston (United Kingdom)" input="*DPClash.CityE.boston-_**Boston.Lincolnshire.UnitedKingdom--"/>
175
+ <value name="{Boston, Georgia, UnitedStates}" desc="Boston (Georgia, USA)" input="*DPClash.CityE.boston-_**Boston.Georgia.UnitedStates--"/>
176
+ <value name="{Boston, Karakalpakstan, Uzbekistan}" desc="Boston (Uzbekistan)" input="*DPClash.CityE.boston-_**Boston.Karakalpakstan.Uzbekistan--"/>
177
+ <value name="{Boston, Indiana, UnitedStates}" desc="Boston (Indiana, USA)" input="*DPClash.CityE.boston-_**Boston.Indiana.UnitedStates--"/>
178
+ <value name="{Boston, DavaoOriental, Philippines}" desc="Boston (Philippines)" input="*DPClash.CityE.boston-_**Boston.DavaoOriental.Philippines--"/>
179
+ </assumption>
180
+ </assumptions>
181
+ <sources count="4">
182
+ <source url="http://www.wolframalpha.com/sources/CityDataSourceInformationNotes.html" text="City data"/>
183
+ <source url="http://www.wolframalpha.com/sources/OpenStreetMapSourceInformationNotes.html" text="Open street map"/>
184
+ <source url="http://www.wolframalpha.com/sources/PeopleDataSourceInformationNotes.html" text="People data"/>
185
+ <source url="http://www.wolframalpha.com/sources/WeatherDataSourceInformationNotes.html" text="Weather data"/>
186
+ </sources>
187
+ </queryresult>
@@ -0,0 +1,34 @@
1
+ require 'bacon'
2
+ require 'bacon/bits'
3
+ require 'rr'
4
+ require 'bacon/rr'
5
+ require 'wolfram'
6
+ Wolfram.appid = 'xxx'
7
+
8
+ module TestHelpers
9
+ def capture_stdout(&block)
10
+ original_stdout = $stdout
11
+ $stdout = fake = StringIO.new
12
+ begin
13
+ yield
14
+ ensure
15
+ $stdout = original_stdout
16
+ end
17
+ fake.string
18
+ end
19
+
20
+ def capture_stderr(&block)
21
+ original_stderr = $stderr
22
+ $stderr = fake = StringIO.new
23
+ begin
24
+ yield
25
+ ensure
26
+ $stderr = original_stderr
27
+ end
28
+ fake.string
29
+ end
30
+ end
31
+
32
+ class Bacon::Context
33
+ include TestHelpers
34
+ end
data/test/util_test.rb ADDED
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Util" do
4
+ it "#to_query generates correct string" do
5
+ Wolfram::Util.to_query('moo', 'sound').should == 'sound=moo'
6
+ end
7
+
8
+ it "#to_param generates correct_string" do
9
+ Wolfram::Util.to_param(:appid => 'xxx', :input => 'from ny to boston').should ==
10
+ "appid=xxx&input=from+ny+to+boston"
11
+ end
12
+
13
+ describe "#module_get" do
14
+ before_all { eval %[module Blah; end] }
15
+
16
+ it "creates a new module if it doesn't exist" do
17
+ Wolfram::Util.module_get(Blah, 'One').should == Blah::One
18
+ end
19
+
20
+ it "returns existing module" do
21
+ Wolfram::Util.module_get(Blah, 'Two')
22
+ Wolfram::Util.module_get(Blah, 'Two').should == Blah::Two
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ describe "Wolfram" do
4
+ def wolfram(input)
5
+ Wolfram.run input.split(/\s+/)
6
+ end
7
+
8
+ describe "#run" do
9
+ it "with no query returns usage" do
10
+ capture_stdout { wolfram('') }.should =~ /^Usage: wolfram/
11
+ end
12
+
13
+ it "with normal query returns output" do
14
+ mock(Wolfram::Query).fetch(anything) {
15
+ File.read(File.expand_path(File.dirname(__FILE__) + '/fixtures/boston.xml'))
16
+ }
17
+ capture_stdout { wolfram 'boston' }.should =~ /boston/i
18
+ end
19
+
20
+ it "with invalid response prints error" do
21
+ mock(Wolfram::Query).fetch(anything) { ' ' }
22
+ capture_stderr { wolfram 'boston' }.should =~ /^Wolfram Error:.*queryresult/
23
+ end
24
+
25
+ it "with no appid prints error" do
26
+ val = Wolfram.appid
27
+ Wolfram.appid = nil
28
+ capture_stderr { wolfram 'boston' }.should =~ /Wolfram Error:.*APPID/
29
+ Wolfram.appid = val
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wolfram
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Gabriel Horner
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-24 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: nokogiri
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 1
30
+ segments:
31
+ - 1
32
+ - 4
33
+ - 3
34
+ version: 1.4.3
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rr
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: bacon
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 19
60
+ segments:
61
+ - 1
62
+ - 1
63
+ - 0
64
+ version: 1.1.0
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: bacon-rr
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id004
81
+ - !ruby/object:Gem::Dependency
82
+ name: bacon-bits
83
+ prerelease: false
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ hash: 3
90
+ segments:
91
+ - 0
92
+ version: "0"
93
+ type: :development
94
+ version_requirements: *id005
95
+ description: Explore the vast world of computational knowledge available for free via Wolfram's v2 API.
96
+ email: gabriel.horner@gmail.com
97
+ executables:
98
+ - wolfram
99
+ extensions: []
100
+
101
+ extra_rdoc_files:
102
+ - README.rdoc
103
+ - LICENSE.txt
104
+ files:
105
+ - lib/wolfram/assumption.rb
106
+ - lib/wolfram/pod.rb
107
+ - lib/wolfram/query.rb
108
+ - lib/wolfram/result.rb
109
+ - lib/wolfram/util.rb
110
+ - lib/wolfram/version.rb
111
+ - lib/wolfram/xml_container.rb
112
+ - lib/wolfram.rb
113
+ - test/test_helper.rb
114
+ - test/util_test.rb
115
+ - test/wolfram_test.rb
116
+ - bin/wolfram
117
+ - LICENSE.txt
118
+ - CHANGELOG.rdoc
119
+ - README.rdoc
120
+ - deps.rip
121
+ - test/deps.rip
122
+ - Rakefile
123
+ - .gemspec
124
+ - test/fixtures/boston.xml
125
+ has_rdoc: true
126
+ homepage: http://github.com/cldwalker/wolfram
127
+ licenses:
128
+ - MIT
129
+ post_install_message:
130
+ rdoc_options: []
131
+
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ hash: 3
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 23
149
+ segments:
150
+ - 1
151
+ - 3
152
+ - 6
153
+ version: 1.3.6
154
+ requirements: []
155
+
156
+ rubyforge_project: tagaholic
157
+ rubygems_version: 1.3.7
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: Wolfram V2 API client
161
+ test_files: []
162
+