kelredd-resourceful 0.1.1

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/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
+