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 CHANGED
@@ -21,7 +21,7 @@ spec = Gem::Specification.new do |s|
21
21
 
22
22
  s.add_dependency('nokogiri')
23
23
  s.add_dependency('json')
24
- s.add_dependency('rest_client')
24
+ s.add_dependency('rest-client')
25
25
  s.add_dependency('log4r')
26
26
  s.add_dependency('kelredd-useful')
27
27
  end
@@ -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
@@ -17,3 +17,4 @@ Given /^I have a configured resource host set to log$/ do
17
17
  RESOURCE_CONFIG[:log]
18
18
  }
19
19
  end
20
+
@@ -1,10 +1,22 @@
1
1
  module Resourceful
2
2
  module Exceptions
3
3
 
4
- class ConfigurationError < ::StandardError
4
+ class ResourcefulError < ::StandardError
5
5
  end
6
6
 
7
- class FormatError < ::StandardError
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
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), "model" ,"*.rb")].each do |file|
2
+ require file
3
+ end
@@ -41,7 +41,7 @@ module Resourceful
41
41
  end
42
42
 
43
43
  def self.build(json_str)
44
- Hash.from_json(json_str.to_s)
44
+ JSON.parse(json_str.to_s)
45
45
  end
46
46
 
47
47
  end
@@ -2,8 +2,8 @@ module Resourceful
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 1
6
- TINY = 1
5
+ MINOR = 2
6
+ TINY = 0
7
7
 
8
8
  def self.to_s # :nodoc:
9
9
  [MAJOR, MINOR, TINY].join('.')
data/lib/resourceful.rb CHANGED
@@ -1,10 +1,7 @@
1
- require 'rubygems'
2
- require 'nokogiri'
3
- require 'json'
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
- 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')
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.1.1
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-21 00:00:00 -07:00
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: rest_client
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