wolfram 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+