kelredd-resourceful 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/features/resource_models.feature +26 -0
- data/features/step_definitions/resource_models_steps.rb +54 -0
- data/features/step_definitions/support/models.rb +89 -0
- data/features/step_definitions/support/resources.rb +1 -0
- data/lib/resourceful/exceptions.rb +14 -2
- data/lib/resourceful/extensions.rb +72 -0
- data/lib/resourceful/model/base.rb +49 -0
- data/lib/resourceful/model/json.rb +48 -0
- data/lib/resourceful/model/xml.rb +59 -0
- data/lib/resourceful/model.rb +3 -0
- data/lib/resourceful/resource/format.rb +1 -1
- data/lib/resourceful/version.rb +2 -2
- data/lib/resourceful.rb +6 -9
- metadata +12 -3
data/Rakefile
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Resource Models
|
2
|
+
In order to consume rest based resources
|
3
|
+
As an object
|
4
|
+
I want to define and initialize a model class from xml resource data
|
5
|
+
|
6
|
+
Scenario: Get JSON user
|
7
|
+
Given I have a configured resource host
|
8
|
+
And I am user with the screen_name "kelredd"
|
9
|
+
When I load my "Json" user model
|
10
|
+
Then the result should be a valid User model
|
11
|
+
|
12
|
+
Scenario: Get XML user
|
13
|
+
Given I have a configured resource host
|
14
|
+
And I am user with the screen_name "kelredd"
|
15
|
+
When I load my "Xml" user model
|
16
|
+
Then the result should be a valid User model
|
17
|
+
|
18
|
+
Scenario: Get JSON status collection
|
19
|
+
Given I have a configured resource host
|
20
|
+
When I load the "Json" status "public_timeline"
|
21
|
+
Then the result should be a collection of valid Status models
|
22
|
+
|
23
|
+
Scenario: Get XML status collection
|
24
|
+
Given I have a configured resource host
|
25
|
+
When I load the "Xml" status "public_timeline"
|
26
|
+
Then the result should be a collection of valid Status models
|
@@ -0,0 +1,54 @@
|
|
1
|
+
Given /^I am user with the screen_name "([^\"]*)"$/ do |screen_name|
|
2
|
+
@screen_name = screen_name
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I load my "([^\"]*)" user model$/ do |klass|
|
6
|
+
name = "User#{klass}"
|
7
|
+
constant = ::Object
|
8
|
+
model_klass = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
9
|
+
@result = model_klass.find(@screen_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I load the "([^\"]*)" status "([^\"]*)"$/ do |klass, collection|
|
13
|
+
name = "Status#{klass}"
|
14
|
+
constant = ::Object
|
15
|
+
model_klass = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
16
|
+
@result = model_klass.find(collection)
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /^the result should be a valid User model$/ do
|
20
|
+
[:id, :name, :screen_name, :location, :description, :profile_image_url, :url, :protected, :followers_count, :friends_count, :created_on, :last_status_at, :last_status].each do |attribute|
|
21
|
+
assert @result.respond_to?(attribute)
|
22
|
+
end
|
23
|
+
{
|
24
|
+
:id => 38225297,
|
25
|
+
:name => "Kelly Redding",
|
26
|
+
:screen_name => "kelredd",
|
27
|
+
:protected => false,
|
28
|
+
:created_on => Date.strptime("2009-05-06")
|
29
|
+
}.each do |k,v|
|
30
|
+
assert_equal v, @result.send(k.to_s)
|
31
|
+
end
|
32
|
+
assert_kind_of DateTime, @result.last_status_at
|
33
|
+
assert !@result.last_status.nil?
|
34
|
+
assert @result.last_status != ''
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /^the result should be a collection of valid Status models$/ do
|
38
|
+
assert_kind_of Array, @result
|
39
|
+
assert_equal 20, @result.length
|
40
|
+
|
41
|
+
[:id, :text, :source, :truncated, :favorited, :reply_status, :reply_user, :user_id, :user_screen_name, :user].each do |attribute|
|
42
|
+
assert @result.first.respond_to?(attribute)
|
43
|
+
assert_nothing_raised do
|
44
|
+
@result.first.send(attribute.to_s)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
[:id, :text, :truncated, :favorited, :user_id, :user_screen_name, :user].each do |attribute|
|
49
|
+
assert !@result.first.send(attribute.to_s).nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_kind_of Resourceful::Model::Base, @result.first.user
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class UserXml < Resourceful::Model::Xml
|
2
|
+
|
3
|
+
def self.find(screen_name, force=false)
|
4
|
+
get("/users/#{screen_name}", {}, "//user")
|
5
|
+
end
|
6
|
+
|
7
|
+
attribute :id, :integer
|
8
|
+
attribute :name, :string
|
9
|
+
attribute :screen_name, :string
|
10
|
+
attribute :location, :string
|
11
|
+
attribute :description, :string
|
12
|
+
attribute :profile_image_url, :string
|
13
|
+
attribute :url, :string
|
14
|
+
attribute :protected, :boolean
|
15
|
+
attribute :followers_count, :integer
|
16
|
+
attribute :friends_count, :integer
|
17
|
+
attribute :created_on, :date, :path => 'created_at'
|
18
|
+
attribute :last_status_at, :datetime, :path => "status/created_at"
|
19
|
+
attribute :last_status, :string, :path => "status/text"
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class UserJson < Resourceful::Model::Json
|
24
|
+
|
25
|
+
def self.find(screen_name, force=false)
|
26
|
+
get("/users/#{screen_name}", {})
|
27
|
+
end
|
28
|
+
|
29
|
+
attribute :id, :integer
|
30
|
+
attribute :name, :string
|
31
|
+
attribute :screen_name, :string
|
32
|
+
attribute :location, :string
|
33
|
+
attribute :description, :string
|
34
|
+
attribute :profile_image_url, :string
|
35
|
+
attribute :url, :string
|
36
|
+
attribute :protected, :boolean
|
37
|
+
attribute :followers_count, :integer
|
38
|
+
attribute :friends_count, :integer
|
39
|
+
attribute :created_on, :date, :path => 'created_at'
|
40
|
+
attribute :last_status_at, :datetime, :path => "status/created_at"
|
41
|
+
attribute :last_status, :string, :path => "status/text"
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class StatusXml < Resourceful::Model::Xml
|
46
|
+
|
47
|
+
def self.find(collection, force=false)
|
48
|
+
get_collection("/statuses/#{collection}", {}, "//statuses/status")
|
49
|
+
end
|
50
|
+
|
51
|
+
attribute :id, :integer
|
52
|
+
attribute :text, :string
|
53
|
+
attribute :source, :string
|
54
|
+
attribute :created_on, :date, :path => 'created_at'
|
55
|
+
attribute :truncated, :boolean
|
56
|
+
attribute :favorited, :boolean
|
57
|
+
attribute :reply_status, :integer, :path => 'in_reply_to_status_id'
|
58
|
+
attribute :reply_user, :integer, :path => 'in_reply_to_user_id'
|
59
|
+
attribute :user_id, :integer, :path => "user/id"
|
60
|
+
attribute :user_screen_name, :integer, :path => "user/screen_name"
|
61
|
+
|
62
|
+
def user
|
63
|
+
@user ||= (get_node('./user') ? UserXml.new(get_node('./user')) : nil)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
class StatusJson < Resourceful::Model::Json
|
69
|
+
|
70
|
+
def self.find(collection, force=false)
|
71
|
+
get_collection("/statuses/#{collection}", {})
|
72
|
+
end
|
73
|
+
|
74
|
+
attribute :id, :integer
|
75
|
+
attribute :text, :string
|
76
|
+
attribute :source, :string
|
77
|
+
attribute :created_on, :date, :path => 'created_at'
|
78
|
+
attribute :truncated, :boolean
|
79
|
+
attribute :favorited, :boolean
|
80
|
+
attribute :reply_status, :integer, :path => 'in_reply_to_status_id'
|
81
|
+
attribute :reply_user, :integer, :path => 'in_reply_to_user_id'
|
82
|
+
attribute :user_id, :integer, :path => "user/id"
|
83
|
+
attribute :user_screen_name, :integer, :path => "user/screen_name"
|
84
|
+
|
85
|
+
def user
|
86
|
+
@user ||= UserXml.find(self.user_screen_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -1,10 +1,22 @@
|
|
1
1
|
module Resourceful
|
2
2
|
module Exceptions
|
3
3
|
|
4
|
-
class
|
4
|
+
class ResourcefulError < ::StandardError
|
5
5
|
end
|
6
6
|
|
7
|
-
class
|
7
|
+
class ResourceError < ResourcefulError
|
8
|
+
end
|
9
|
+
|
10
|
+
class ConfigurationError < ResourceError
|
11
|
+
end
|
12
|
+
|
13
|
+
class FormatError < ResourceError
|
14
|
+
end
|
15
|
+
|
16
|
+
class ModelError < ResourcefulError
|
17
|
+
end
|
18
|
+
|
19
|
+
class AttributeError < ModelError
|
8
20
|
end
|
9
21
|
|
10
22
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Extensions
|
3
|
+
module String
|
4
|
+
|
5
|
+
module ClassMethods; end
|
6
|
+
def self.included(klass)
|
7
|
+
klass.extend(ClassMethods) if klass.kind_of?(Class)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
unless "".respond_to?(:to_datetime)
|
14
|
+
def to_datetime
|
15
|
+
::DateTime.civil(*::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec).map { |arg| arg || 0 }) rescue nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
unless "".respond_to?(:to_datetime)
|
20
|
+
def to_date
|
21
|
+
::Date.civil(*::Date._parse(self, false).values_at(:year, :mon, :mday).map { |arg| arg || 0 }) rescue nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
unless "".respond_to?(:to_boolean)
|
26
|
+
def to_boolean
|
27
|
+
self =~ /^(true|1)$/i ? true : false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Resourceful
|
36
|
+
module Extensions
|
37
|
+
module Hash
|
38
|
+
|
39
|
+
module ClassMethods; end
|
40
|
+
def self.included(klass)
|
41
|
+
klass.extend(ClassMethods) if klass.kind_of?(Class)
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns string formatted for HTTP URL encoded name-value pairs.
|
48
|
+
# For example,
|
49
|
+
# {:id => 'thomas_hardy'}.to_http_query_str
|
50
|
+
# # => "?id=thomas_hardy"
|
51
|
+
# {:id => 23423, :since => Time.now}.to_http_query_str
|
52
|
+
# # => "?since=Thu,%2021%20Jun%202007%2012:10:05%20-0500&id=23423"
|
53
|
+
unless {}.respond_to?(:to_http_query_str)
|
54
|
+
def to_http_query_str(opts = {})
|
55
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
56
|
+
opts[:prepend] ||= '?'
|
57
|
+
opts[:append] ||= ''
|
58
|
+
self.empty? ? '' : "#{opts[:prepend]}#{self.collect{|key, val| "#{key.to_s}=#{CGI.escape(val.to_s)}"}.join('&')}#{opts[:append]}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class String
|
67
|
+
include Resourceful::Extensions::String
|
68
|
+
end
|
69
|
+
|
70
|
+
class Hash
|
71
|
+
include Resourceful::Extensions::Hash
|
72
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Base
|
5
|
+
|
6
|
+
# KDR: override this to have a per model Resource handler
|
7
|
+
# => TODO: need to support creating resource instances first though
|
8
|
+
def self.resource
|
9
|
+
Resourceful::Resource
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.get(path, opts={}, force=false)
|
13
|
+
resource.get(path, opts, force)
|
14
|
+
end
|
15
|
+
def self.get_collection(path, opts={}, force=false)
|
16
|
+
(yield resource.get(path, opts, force)).collect{|data| new(data)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def self.attribute(name, type, config)
|
25
|
+
content_method = case type.to_sym
|
26
|
+
when :string
|
27
|
+
'to_s'
|
28
|
+
when :integer
|
29
|
+
'to_i'
|
30
|
+
when :float
|
31
|
+
'to_f'
|
32
|
+
when :date
|
33
|
+
'to_date'
|
34
|
+
when :datetime
|
35
|
+
'to_datetime'
|
36
|
+
when :boolean
|
37
|
+
'to_boolean'
|
38
|
+
else
|
39
|
+
'to_s'
|
40
|
+
end
|
41
|
+
define_method(name) do
|
42
|
+
instance_variable_get("@#{name}") || instance_variable_set("@#{name}", ((a = attribute(config)) && a.kind_of?(String)) ? a.send(content_method) : a)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Json < Resourceful::Model::Base
|
5
|
+
|
6
|
+
attr_reader :json
|
7
|
+
|
8
|
+
def self.get(path, params, force=false)
|
9
|
+
opts = {
|
10
|
+
:format => 'json',
|
11
|
+
:params => params || {}
|
12
|
+
}
|
13
|
+
new(super(path, opts, force))
|
14
|
+
end
|
15
|
+
def self.get_collection(path, params, force=false)
|
16
|
+
opts = {
|
17
|
+
:format => 'json',
|
18
|
+
:params => params || {}
|
19
|
+
}
|
20
|
+
super(path, opts, force) do |data|
|
21
|
+
data
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(json)
|
26
|
+
@json = json
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def self.attribute(name, type, config={})
|
32
|
+
config[:path] ||= name
|
33
|
+
super(name, type, config)
|
34
|
+
end
|
35
|
+
|
36
|
+
def attribute(config)
|
37
|
+
paths = config[:path].to_s.split('/')
|
38
|
+
paths.inject(@json) do |val,path|
|
39
|
+
#raise Resourceful::Exceptions::AttributeError, "no matching attribute data for'#{config[:path]}'" if val.nil?
|
40
|
+
val.fetch(path, nil) rescue nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Resourceful
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Xml < Resourceful::Model::Base
|
5
|
+
|
6
|
+
attr_reader :xml
|
7
|
+
|
8
|
+
def self.get(path, params, xpath, force=false)
|
9
|
+
opts = {
|
10
|
+
:format => 'xml',
|
11
|
+
:params => params || {}
|
12
|
+
}
|
13
|
+
new(super(path, opts, force).xpath(xpath))
|
14
|
+
end
|
15
|
+
def self.get_collection(path, params, xpath, force=false)
|
16
|
+
opts = {
|
17
|
+
:format => 'xml',
|
18
|
+
:params => params || {}
|
19
|
+
}
|
20
|
+
super(path, opts, force) do |data|
|
21
|
+
data.xpath(xpath)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(xml)
|
26
|
+
@xml = xml
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def self.attribute(name, type, config={})
|
32
|
+
config[:path] ||= name
|
33
|
+
super(name, type, config)
|
34
|
+
end
|
35
|
+
|
36
|
+
def attribute(config)
|
37
|
+
node = get_node("./#{config[:path]}")
|
38
|
+
#raise Resourceful::Exceptions::AttributeError, "no matching attribute data for'#{config[:path]}'" unless node
|
39
|
+
node.content rescue nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_node(xml, path)
|
43
|
+
xml.xpath(path.to_s).first
|
44
|
+
end
|
45
|
+
def get_node(path)
|
46
|
+
self.class.get_node(@xml, path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.xml_root_name(xml)
|
50
|
+
xml.root.name
|
51
|
+
end
|
52
|
+
def xml_root_name
|
53
|
+
self.class.xml_root_name(@xml)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/resourceful/version.rb
CHANGED
data/lib/resourceful.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
require 'rest_client'
|
5
|
-
require 'log4r'
|
6
|
-
require 'useful/ruby_extensions'
|
1
|
+
%w(rubygems nokogiri json rest_client log4r).each do |lib|
|
2
|
+
require lib
|
3
|
+
end
|
7
4
|
|
8
|
-
|
9
|
-
require File.join(File.dirname(__FILE__), 'resourceful',
|
10
|
-
|
5
|
+
%w(exceptions extensions resource model).each do |file|
|
6
|
+
require File.join(File.dirname(__FILE__), 'resourceful', "#{file}.rb")
|
7
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kelredd-resourceful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kelly Redding
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
version: "0"
|
34
34
|
version:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
36
|
+
name: rest-client
|
37
37
|
type: :runtime
|
38
38
|
version_requirement:
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -75,15 +75,24 @@ files:
|
|
75
75
|
- Rakefile
|
76
76
|
- features/config_resource.feature
|
77
77
|
- features/get_resource.feature
|
78
|
+
- features/resource_models.feature
|
78
79
|
- features/step_definitions
|
79
80
|
- features/step_definitions/resource_config_steps.rb
|
80
81
|
- features/step_definitions/resource_get_steps.rb
|
82
|
+
- features/step_definitions/resource_models_steps.rb
|
81
83
|
- features/step_definitions/support
|
82
84
|
- features/step_definitions/support/env.rb
|
83
85
|
- features/step_definitions/support/helpers.rb
|
86
|
+
- features/step_definitions/support/models.rb
|
84
87
|
- features/step_definitions/support/resources.rb
|
85
88
|
- lib/resourceful
|
86
89
|
- lib/resourceful/exceptions.rb
|
90
|
+
- lib/resourceful/extensions.rb
|
91
|
+
- lib/resourceful/model
|
92
|
+
- lib/resourceful/model/base.rb
|
93
|
+
- lib/resourceful/model/json.rb
|
94
|
+
- lib/resourceful/model/xml.rb
|
95
|
+
- lib/resourceful/model.rb
|
87
96
|
- lib/resourceful/resource
|
88
97
|
- lib/resourceful/resource/base.rb
|
89
98
|
- lib/resourceful/resource/cache.rb
|