kelredd-resourceful 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|