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