kelredd-resourceful 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,69 @@
1
+ = Resourceful
2
+
3
+ == Description
4
+
5
+ A ruby gem to abstract web resource handling. You configure the server, resource format, etc... and
6
+ the gem will provide interactions with that resource in a restful manner, with data returned in a
7
+ de-serialized object format.
8
+
9
+ The supported formats are:
10
+ * JSON (objects returned as Ruby Hash)
11
+ * XML (objects returned as Nokogiri Xml object)
12
+
13
+ == Installation
14
+
15
+ sudo gem install kelredd-resourceful --source http://gems.github.com
16
+
17
+ == Dependencies
18
+
19
+ * nokogiri (xml resource format handling)
20
+ * json (json resource format handling)
21
+ * rest_client (rest web resource access)
22
+ * log4r (for resource interaction logging)
23
+ * kelredd-useful (some nice ruby helpers)
24
+
25
+ == Usage
26
+
27
+ # Single host resource handling
28
+ require 'resourceful'
29
+ Resourceful::Resource.configure :host => 'localhost:3000'
30
+ Resourceful::Resource::Cache.clear
31
+ Resourceful::Resource.get '/widgets', :format => 'json' # specify explicit format
32
+ Resourceful::Resource.get '/widgets.json' # or, imply format from resource request
33
+
34
+ # TODO: extend to create Resource instances that have per instance host, path, format settings
35
+
36
+ == Testing
37
+ A suite of cucumber features are available for you to run as an acceptance test. You should look to the features
38
+ for additional documentation and usage scenarios. To run the features:
39
+
40
+ # make sure you have the cucumber gem
41
+ $ sudo gem install cucumber
42
+ # change to the resourceful gem installed location
43
+ $ cd /opt/local/lib/ruby/gems/x.x/gems/kelredd-resourceful-xxx
44
+ $ rake features
45
+
46
+ == License
47
+
48
+ Copyright (c) 2009 Kelly Redding
49
+
50
+ Permission is hereby granted, free of charge, to any person
51
+ obtaining a copy of this software and associated documentation
52
+ files (the "Software"), to deal in the Software without
53
+ restriction, including without limitation the rights to use,
54
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
55
+ copies of the Software, and to permit persons to whom the
56
+ Software is furnished to do so, subject to the following
57
+ conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
64
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
65
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
66
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
67
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
68
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
69
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+
5
+ require 'lib/resourceful/version'
6
+
7
+ task :default => :test
8
+
9
+ spec = Gem::Specification.new do |s|
10
+ s.name = 'resourceful'
11
+ s.version = Resourceful::Version.to_s
12
+ s.has_rdoc = true
13
+ s.extra_rdoc_files = %w(README.rdoc)
14
+ s.rdoc_options = %w(--main README.rdoc)
15
+ s.summary = "A ruby gem to abstract web resource handling."
16
+ s.author = 'Kelly Redding'
17
+ s.email = 'kelly@kelredd.com'
18
+ s.homepage = ''
19
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{features,lib,test}/**/*")
20
+ # s.executables = ['resourceful']
21
+
22
+ s.add_dependency('nokogiri')
23
+ s.add_dependency('json')
24
+ s.add_dependency('rest_client')
25
+ s.add_dependency('log4r')
26
+ s.add_dependency('kelredd-useful')
27
+ end
28
+
29
+ Rake::GemPackageTask.new(spec) do |pkg|
30
+ pkg.gem_spec = spec
31
+ end
32
+
33
+ Rake::TestTask.new do |t|
34
+ t.libs << 'test'
35
+ t.test_files = FileList["test/**/*_test.rb"]
36
+ t.verbose = true
37
+ end
38
+
39
+ desc 'Generate the gemspec to serve this Gem from Github'
40
+ task :github do
41
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
42
+ File.open(file, 'w') {|f| f << spec.to_ruby }
43
+ puts "Created gemspec: #{file}"
44
+ end
45
+
46
+ require 'cucumber'
47
+ require 'cucumber/rake/task'
48
+
49
+ Cucumber::Rake::Task.new(:features) do |t|
50
+ t.cucumber_opts = "features --format pretty"
51
+ end
@@ -0,0 +1,29 @@
1
+ Feature: Config Resource
2
+ In order to consume rest based resources
3
+ As an user
4
+ I want to configure Resourceful for a particular resource format and host
5
+
6
+ Scenario: JSON Format definition
7
+ Given I want to use the json resource format
8
+ Then the format should be .json
9
+
10
+ Scenario: XML Format definition
11
+ Given I want to use the xml resource format
12
+ Then the format should be .xml
13
+
14
+ Scenario: Host not configured
15
+ Given I have no host server configured
16
+ Then resourceful should complain about a configuration error
17
+
18
+ Scenario: Host configured
19
+ Given I have a configured resource host
20
+ Then the host should be set
21
+
22
+ Scenario: Resource logger
23
+ Given I have a configured resource host set to log
24
+ Then verify the log settings
25
+
26
+ Scenario: Resource logging
27
+ Given I have a configured resource host set to log
28
+ When I get a json formatted resource
29
+ Then the set log file should exist
@@ -0,0 +1,34 @@
1
+ Feature: Get Resource
2
+ In order to consume rest based resources
3
+ As an object
4
+ I want to get data from a web resource as a formatted object
5
+
6
+ Scenario: Get JSON resource
7
+ Given I have a configured resource host
8
+ When I get a json formatted resource
9
+ Then the result should be a hash object
10
+
11
+ Scenario: Get XML resource
12
+ Given I have a configured resource host
13
+ When I get an xml formatted resource
14
+ Then the result should be an xml object
15
+
16
+ Scenario: Get an implicit format JSON resource
17
+ Given I have a configured resource host
18
+ When I get a json formatted implicitly resource
19
+ Then the result should be a hash object
20
+
21
+ Scenario: Get an implicit format XML resource
22
+ Given I have a configured resource host
23
+ When I get an xml formatted implicitly resource
24
+ Then the result should be an xml object
25
+
26
+ Scenario: Get resource with format not supported
27
+ Given I have a configured resource host
28
+ When I get an poop formatted resource
29
+ Then resourceful should complain about a format error
30
+
31
+ Scenario: Get non existent resource
32
+ Given I have a configured resource host
33
+ When I get a resource that does not exist
34
+ Then resourceful should complain about the resource not being found
@@ -0,0 +1,23 @@
1
+ Then /^the format should be \.(.+)$/ do |format|
2
+ assert_equal @format.to_s, format
3
+ end
4
+
5
+ Then /^the host should be set$/ do
6
+ assert_equal Resourceful::Resource.host, RESOURCE_CONFIG[:host]
7
+ end
8
+
9
+ Then /^resourceful should complain about a configuration error$/ do
10
+ assert_raise Resourceful::Exceptions::ConfigurationError do
11
+ Resourceful::Resource::Base.get RESOURCE_CONFIG[:resource], :format => 'json', :params => RESOURCE_CONFIG[:params]
12
+ end
13
+ end
14
+
15
+ Then /^verify the log settings$/ do
16
+ assert_equal Resourceful::Resource.logger.outputters.detect{|out| out.respond_to?('filename')}.filename, RESOURCE_CONFIG[:log]
17
+ end
18
+
19
+ Then /^the set log file should exist$/ do
20
+ log_file = File.expand_path(RESOURCE_CONFIG[:log])
21
+ assert File.exists?(log_file)
22
+ FileUtils.rm(log_file) if File.exists?(log_file)
23
+ end
@@ -0,0 +1,37 @@
1
+ When /^I get a[n]* (.+) formatted resource$/ do |format|
2
+ @result = ResourcefulFeature::Helpers.safe_run_get do
3
+ Resourceful::Resource::Base.get RESOURCE_CONFIG[:resource], :format => format, :params => RESOURCE_CONFIG[:params]
4
+ end
5
+ end
6
+
7
+ When /^I get a[n]* (.+) formatted implicitly resource$/ do |format|
8
+ @result = ResourcefulFeature::Helpers.safe_run_get do
9
+ Resourceful::Resource::Base.get RESOURCE_CONFIG[:resource]+".#{format}", :params => RESOURCE_CONFIG[:params]
10
+ end
11
+ end
12
+
13
+ When /^I get a resource that does not exist$/ do
14
+ @result = ResourcefulFeature::Helpers.safe_run_get do
15
+ Resourceful::Resource::Base.get '/unknown', :format => 'xml', :params => RESOURCE_CONFIG[:params]
16
+ end
17
+ end
18
+
19
+ Then /^the result should be a hash object$/ do
20
+ assert_kind_of Hash, @result.first
21
+ end
22
+
23
+ Then /^the result should be an xml object$/ do
24
+ assert_kind_of Nokogiri::XML::Document, @result
25
+ end
26
+
27
+ Then /^resourceful should complain about a format error$/ do
28
+ assert @result
29
+ assert_kind_of Resourceful::Exceptions::FormatError, @result
30
+ assert @result.message.length > 0
31
+ end
32
+
33
+ Then /^resourceful should complain about the resource not being found$/ do
34
+ assert @result
35
+ assert_kind_of RestClient::ResourceNotFound, @result
36
+ assert @result.message.length > 0
37
+ end
@@ -0,0 +1,11 @@
1
+ require 'test/unit/assertions'
2
+ World(Test::Unit::Assertions)
3
+
4
+ RESOURCE_CONFIG = {
5
+ :host => 'twitter.com',
6
+ :resource => '/statuses/public_timeline',
7
+ :params => {},
8
+ :log => "./test.log"
9
+ }
10
+
11
+ require File.dirname(__FILE__) + '/../../../lib/resourceful'
@@ -0,0 +1,13 @@
1
+ module ResourcefulFeature
2
+ module Helpers
3
+
4
+ def self.safe_run_get
5
+ begin
6
+ yield
7
+ rescue Exception => err
8
+ err
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ Given /^I want to use the (.+) resource format$/ do |format|
2
+ @format = Resourceful::Resource::Format.get(format)
3
+ end
4
+
5
+ Given /^I have no host server configured$/ do
6
+ Resourceful::Resource::Base.configure
7
+ end
8
+
9
+ Given /^I have a configured resource host$/ do
10
+ Resourceful::Resource::Base.configure(:host => RESOURCE_CONFIG[:host])
11
+ end
12
+
13
+ Given /^I have a configured resource host set to log$/ do
14
+ log_file = File.expand_path(RESOURCE_CONFIG[:log])
15
+ FileUtils.rm(log_file) if File.exists?(log_file)
16
+ Resourceful::Resource::Base.configure(:host => RESOURCE_CONFIG[:host]) {
17
+ RESOURCE_CONFIG[:log]
18
+ }
19
+ end
@@ -0,0 +1,11 @@
1
+ module Resourceful
2
+ module Exceptions
3
+
4
+ class ConfigurationError < ::StandardError
5
+ end
6
+
7
+ class FormatError < ::StandardError
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,84 @@
1
+ require 'rest_client'
2
+ require 'log4r'
3
+ require File.join(File.dirname(__FILE__), 'cache.rb')
4
+ require File.join(File.dirname(__FILE__), 'format.rb')
5
+ require File.join(File.dirname(__FILE__), '..', 'exceptions.rb')
6
+
7
+ module Resourceful
8
+ module Resource
9
+ class Base
10
+
11
+ @@host = nil
12
+ @@logger = nil
13
+
14
+ def self.configure(opts={})
15
+ Resourceful::Resource::Cache.clear
16
+ @@logger = Log4r::Logger.new('Resourceful Base')
17
+ @@logger.add(Log4r::StdoutOutputter.new('console'))
18
+ if block_given?
19
+ log_file = yield
20
+ @@logger.add(Log4r::FileOutputter.new('fileOutputter', :filename => log_file, :trunc => false, :formatter => Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %m"))) rescue nil
21
+ RestClient.log = log_file
22
+ end
23
+ @@host = opts[:host]
24
+ end
25
+
26
+ def self.host
27
+ @@host
28
+ end
29
+
30
+ def self.logger
31
+ @@logger
32
+ end
33
+
34
+ # KDR: returns the resource specified in hash form
35
+ def self.get(path, opts={}, force=false)
36
+ path, opts = check_config(path, opts)
37
+ @@rest_client ||= ::RestClient::Resource.new("http://#{@@host}")
38
+ format = Resourceful::Resource::Format.get(opts[:format])
39
+ request_summary = summary('get', path, format, opts[:params])
40
+ cache = Resourceful::Resource::Cache.new(@@host, 'get', path, format, opts[:params])
41
+
42
+ if force || (resp = cache.read).nil?
43
+ log "Resource call: #{request_summary}"
44
+ resp = cache.write(@@rest_client[rest_path(path, format, opts[:params])].get)
45
+ else
46
+ log "Resource call: [CACHE] #{request_summary}"
47
+ end
48
+ format.build(resp)
49
+ end
50
+
51
+ private
52
+
53
+ def self.summary(verb, path, format, params) # :nodoc:
54
+ "#{verb.upcase} #{@@rest_client.url}#{rest_path(path, format, params)}"
55
+ end
56
+
57
+ def self.rest_path(path, format, params) # :nodoc:
58
+ "#{path}.#{format}#{params.to_http_query_str unless params.empty?}"
59
+ end
60
+
61
+ def self.check_config(path, opts)
62
+ raise Resourceful::Exceptions::ConfigurationError, "please configure a host for Resourceful resources" unless @@host
63
+ if path =~ /^(.+)\.(.+)$/
64
+ path = $1
65
+ opts[:format] ||= $2
66
+ end
67
+ opts[:format] ||= Resourceful::Resource::Json.to_s
68
+ opts[:params] ||= {}
69
+ [path, opts]
70
+ end
71
+
72
+ def self.log(msg, level = :info) # :nodoc:
73
+ if(msg)
74
+ if @@logger && @@logger.respond_to?(level)
75
+ @@logger.send(level.to_s, msg)
76
+ else
77
+ puts "** [#{level.to_s.upcase}]: #{msg}"
78
+ end
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,47 @@
1
+ module Resourceful
2
+ module Resource
3
+ class Cache
4
+
5
+ @@cache = {}
6
+
7
+ def self.clear(key=nil)
8
+ if key
9
+ @@cache[key] = nil
10
+ else
11
+ @@cache = {}
12
+ end
13
+ end
14
+
15
+ def self.read(key)
16
+ @@cache[key]
17
+ end
18
+
19
+ def self.write(key, value)
20
+ @@cache[key] = value
21
+ end
22
+
23
+ def self.key(server, verb, path, format, params)
24
+ "#{server}_#{verb}_#{path}_#{format.to_s}_#{params.to_s}"
25
+ end
26
+
27
+ attr_reader :key
28
+
29
+ def initialize(server, verb, path, format, params)
30
+ @key = self.class.key(server, verb, path, format, params)
31
+ end
32
+
33
+ def read
34
+ self.class.read(@key)
35
+ end
36
+
37
+ def write(value)
38
+ self.class.write(@key, value)
39
+ end
40
+
41
+ def clear
42
+ self.class.clear(@key)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,65 @@
1
+ require 'nokogiri'
2
+ require 'json'
3
+
4
+ module Resourceful
5
+ module Resource
6
+ class Format
7
+
8
+ SUPPORTED = ['json', 'xml']
9
+
10
+ def self.get(format)
11
+ raise Resourceful::Exceptions::FormatError, "the format '#{format}' is not supported" unless SUPPORTED.include?(format.to_s)
12
+ self.send(format)
13
+ end
14
+
15
+ def self.json
16
+ Resourceful::Resource::Json
17
+ end
18
+
19
+ def self.xml
20
+ Resourceful::Resource::Xml
21
+ end
22
+
23
+ def self.to_s
24
+ raise 'method not implemented by this format'
25
+ end
26
+
27
+ def self.build(str)
28
+ raise 'method not implemented by this format'
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
35
+ module Resourceful
36
+ module Resource
37
+ class Json < Resourceful::Resource::Format
38
+
39
+ def self.to_s
40
+ "json"
41
+ end
42
+
43
+ def self.build(json_str)
44
+ Hash.from_json(json_str.to_s)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+
51
+ module Resourceful
52
+ module Resource
53
+ class Xml < Resourceful::Resource::Format
54
+
55
+ def self.to_s
56
+ "xml"
57
+ end
58
+
59
+ def self.build(xml_str)
60
+ Nokogiri::XML(xml_str.to_s)
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ Dir[File.join(File.dirname(__FILE__), "resource" ,"*.rb")].each do |file|
2
+ require file
3
+ end
4
+
5
+ module Resourceful
6
+ module Resource
7
+
8
+ def self.configure(opts={}, &block)
9
+ Resourceful::Resource::Base.configure(opts, &block)
10
+ end
11
+
12
+ def self.get(resource, opts={}, force=false)
13
+ Resourceful::Resource::Base.get(resource, opts, force)
14
+ end
15
+
16
+ def self.host
17
+ Resourceful::Resource::Base.host
18
+ end
19
+
20
+ def self.logger
21
+ Resourceful::Resource::Base.logger
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module Resourceful
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 1
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+ require 'json'
4
+ require 'rest_client'
5
+ require 'log4r'
6
+ require 'useful/ruby_extensions'
7
+
8
+ require File.join(File.dirname(__FILE__), 'resourceful', 'exceptions.rb')
9
+ require File.join(File.dirname(__FILE__), 'resourceful', 'resource.rb')
10
+ #require File.join(File.dirname(__FILE__), 'resourceful', 'resource.rb')
@@ -0,0 +1,9 @@
1
+ # http://sneaq.net/textmate-wtf
2
+ $:.reject! { |e| e.include? 'TextMate' }
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'shoulda'
7
+
8
+ require File.dirname(__FILE__) + '/../lib/resourceful'
9
+
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kelredd-resourceful
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Kelly Redding
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rest_client
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: log4r
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: kelredd-useful
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ description:
66
+ email: kelly@kelredd.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - README.rdoc
73
+ files:
74
+ - README.rdoc
75
+ - Rakefile
76
+ - features/config_resource.feature
77
+ - features/get_resource.feature
78
+ - features/step_definitions
79
+ - features/step_definitions/resource_config_steps.rb
80
+ - features/step_definitions/resource_get_steps.rb
81
+ - features/step_definitions/support
82
+ - features/step_definitions/support/env.rb
83
+ - features/step_definitions/support/helpers.rb
84
+ - features/step_definitions/support/resources.rb
85
+ - lib/resourceful
86
+ - lib/resourceful/exceptions.rb
87
+ - lib/resourceful/resource
88
+ - lib/resourceful/resource/base.rb
89
+ - lib/resourceful/resource/cache.rb
90
+ - lib/resourceful/resource/format.rb
91
+ - lib/resourceful/resource.rb
92
+ - lib/resourceful/version.rb
93
+ - lib/resourceful.rb
94
+ - test/test_helper.rb
95
+ has_rdoc: true
96
+ homepage: ""
97
+ post_install_message:
98
+ rdoc_options:
99
+ - --main
100
+ - README.rdoc
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: "0"
108
+ version:
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: "0"
114
+ version:
115
+ requirements: []
116
+
117
+ rubyforge_project:
118
+ rubygems_version: 1.2.0
119
+ signing_key:
120
+ specification_version: 2
121
+ summary: A ruby gem to abstract web resource handling.
122
+ test_files: []
123
+