smg 0.2.1 → 0.2.2

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.
Files changed (42) hide show
  1. data/README.rdoc +1 -1
  2. data/lib/smg/http.rb +23 -24
  3. data/lib/smg/http/exceptions.rb +5 -9
  4. data/lib/smg/http/hooks.rb +43 -0
  5. data/lib/smg/http/request.rb +23 -2
  6. data/lib/smg/mapping.rb +1 -3
  7. data/lib/smg/mapping/element.rb +1 -5
  8. data/lib/smg/mapping/typecasts.rb +1 -1
  9. data/lib/smg/model.rb +4 -4
  10. data/lib/smg/version.rb +1 -1
  11. metadata +32 -37
  12. data/examples/crazy.rb +0 -37
  13. data/examples/discogs/label.rb +0 -62
  14. data/examples/discogs/search.rb +0 -60
  15. data/examples/helper.rb +0 -10
  16. data/examples/plant.rb +0 -88
  17. data/examples/twitter.rb +0 -38
  18. data/examples/weather.rb +0 -147
  19. data/spec/collect_spec.rb +0 -272
  20. data/spec/context_spec.rb +0 -189
  21. data/spec/extract_spec.rb +0 -219
  22. data/spec/filtering_spec.rb +0 -164
  23. data/spec/fixtures/discogs/948224.xml +0 -1
  24. data/spec/fixtures/discogs/Enzyme+Records.xml +0 -9
  25. data/spec/fixtures/discogs/Genosha+Recordings.xml +0 -13
  26. data/spec/fixtures/discogs/Ophidian.xml +0 -6
  27. data/spec/fixtures/fake/malus.xml +0 -18
  28. data/spec/fixtures/fake/valve.xml +0 -8
  29. data/spec/fixtures/twitter/pipopolam.xml +0 -46
  30. data/spec/fixtures/yahoo.weather.com.xml +0 -50
  31. data/spec/http/request_spec.rb +0 -186
  32. data/spec/http/shared/automatic.rb +0 -43
  33. data/spec/http/shared/non_automatic.rb +0 -36
  34. data/spec/http/shared/redirectable.rb +0 -30
  35. data/spec/http_spec.rb +0 -76
  36. data/spec/lib/helpers/http_helpers.rb +0 -27
  37. data/spec/lib/matchers/instance_methods.rb +0 -38
  38. data/spec/mapping/element_spec.rb +0 -241
  39. data/spec/mapping/typecasts_spec.rb +0 -52
  40. data/spec/resource_spec.rb +0 -30
  41. data/spec/root_spec.rb +0 -26
  42. data/spec/spec_helper.rb +0 -23
@@ -20,7 +20,7 @@ http://github.com/SSDany/smg/tree/master/examples
20
20
  == REQUIREMENTS:
21
21
 
22
22
  * nokogiri (>=1.3)
23
- * addressable (>=2.1.1)
23
+ * addressable (>=2.1.2)
24
24
 
25
25
  == LICENSE:
26
26
 
@@ -1,11 +1,22 @@
1
1
  require 'smg/http/request'
2
2
  require 'smg/http/exceptions'
3
+ require 'smg/http/hooks'
3
4
 
4
5
  module SMG #:nodoc:
5
- module HTTP
6
+ module HTTP #:nodoc:
7
+
8
+ VERBS = Hash[ Request::VERBS.map { |v| v.to_s.gsub(/^.*::/,'').downcase.to_sym }.zip(Request::VERBS) ]
6
9
 
7
10
  module Model
8
11
 
12
+ HTTP::VERBS.keys.each do |verb|
13
+ self.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
14
+ def #{verb}(path, options = {})
15
+ http(:#{verb}, path, options)
16
+ end
17
+ EOS
18
+ end
19
+
9
20
  def site(value)
10
21
  @site = value
11
22
  end
@@ -14,37 +25,24 @@ module SMG #:nodoc:
14
25
  @params = value
15
26
  end
16
27
 
17
- def get(path, options = {}, &block)
18
- http Net::HTTP::Get, path, options, &block
19
- end
20
-
21
- def head(path, options = {}, &block)
22
- http Net::HTTP::Head, path, options, &block
23
- end
24
-
25
- def delete(path, options = {}, &block)
26
- http Net::HTTP::Delete, path, options, &block
27
- end
28
-
29
- def post(path, options = {}, &block)
30
- http Net::HTTP::Post, path, options, &block
31
- end
32
-
33
- def put(path, options = {}, &block)
34
- http Net::HTTP::Put, path, options, &block
28
+ def on_parse(&block)
29
+ raise ArgumentError, "No block given" unless block_given?
30
+ @on_parse = block
35
31
  end
36
32
 
37
33
  private
38
34
 
39
35
  def http(verb, path, options = {})
40
- raise "site URI missed" unless @site
41
- opts = options.dup
42
- uri = uri_for(path, opts.delete(:query))
43
- response = SMG::HTTP::Request.new(verb, uri, opts).perform
44
- parse block_given? ? yield(response) : response.body
36
+ options = options.dup
37
+ request = SMG::HTTP::Request.new(HTTP::VERBS[verb], uri_for(path,options.delete(:query)), options)
38
+ run_callbacks(:before_request, request, :verb => verb)
39
+ response = request.perform
40
+ run_callbacks(:after_request, response, :verb => verb)
41
+ parse @on_parse ? @on_parse[response.body] : response.body
45
42
  end
46
43
 
47
44
  def uri_for(path, query = nil)
45
+ raise "site URI missed" unless @site
48
46
  ret = Addressable::URI.parse(@site)
49
47
  ret.path = path
50
48
  qvalues = {}
@@ -58,6 +56,7 @@ module SMG #:nodoc:
58
56
 
59
57
  def self.append_features(base)
60
58
  base.extend Model
59
+ base.extend Hooks
61
60
  end
62
61
 
63
62
  end
@@ -3,6 +3,8 @@ module SMG #:nodoc:
3
3
 
4
4
  class ConnectionError < StandardError
5
5
 
6
+ attr_reader :response
7
+
6
8
  def initialize(response, message = nil)
7
9
  @response = response
8
10
  @message = message
@@ -19,16 +21,10 @@ module SMG #:nodoc:
19
21
  class RedirectionError < ConnectionError
20
22
  end
21
23
 
22
- class TimeoutError < ConnectionError
23
-
24
- def initialize(message)
25
- @message = message
26
- end
27
-
28
- def to_s
29
- @message
30
- end
24
+ class TimeoutError < ::Timeout::Error
25
+ end
31
26
 
27
+ class SSLError < StandardError
32
28
  end
33
29
 
34
30
  end
@@ -0,0 +1,43 @@
1
+ module SMG #:nodoc:
2
+ module HTTP #:nodoc:
3
+ module Hooks
4
+
5
+ def before_request(options = {}, &block)
6
+ callback(:before_request, options, &block)
7
+ end
8
+
9
+ def after_request(options = {}, &block)
10
+ callback(:after_request, options, &block)
11
+ end
12
+
13
+ private
14
+
15
+ def run_callbacks(kind, *args)
16
+ options = Hash === args.last ? args.pop : {}
17
+ callbacks(kind).each do |callback, conditions|
18
+ next unless conditions.all? { |k,v| Array === v ? v.include?(options[k]) : options[k] == v }
19
+ callback[*args]
20
+ end
21
+ end
22
+
23
+ def callback(kind, options = {}, &block)
24
+ raise ArgumentError, "Block not given" unless block_given?
25
+ conditions = {}
26
+ if options.key?(:only)
27
+ conditions[:verb] = Array(options[:only]) & HTTP::VERBS.keys
28
+ elsif options.key?(:except)
29
+ conditions[:verb] = HTTP::VERBS.keys - Array(options[:except])
30
+ end
31
+ callbacks(kind) << [block, conditions]
32
+ end
33
+
34
+ def callbacks(kind)
35
+ @callbacks ||= {}
36
+ @callbacks[kind] ||= []
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+
43
+ # EOF
@@ -1,4 +1,5 @@
1
1
  require 'net/http'
2
+ require 'net/https'
2
3
 
3
4
  gem 'addressable', '>= 2.1.1'
4
5
  require 'addressable/uri'
@@ -23,6 +24,7 @@ module SMG #:nodoc:
23
24
  @limit = options[:no_follow] ? 1 : options[:limit] || DEFAULT_LIMIT
24
25
  @body = options[:body]
25
26
  @timeout = options[:timeout] ? options[:timeout].to_i : nil
27
+ @pem = options[:pem]
26
28
  end
27
29
 
28
30
  def perform
@@ -31,6 +33,8 @@ module SMG #:nodoc:
31
33
  handle_response(response)
32
34
  rescue Timeout::Error => e
33
35
  raise TimeoutError, e.message
36
+ rescue OpenSSL::SSL::SSLError => e
37
+ raise SSLError, e.message
34
38
  end
35
39
 
36
40
  protected
@@ -101,6 +105,12 @@ module SMG #:nodoc:
101
105
  end
102
106
  end
103
107
 
108
+ def ssl?
109
+ @uri.scheme == "https"
110
+ end
111
+
112
+ alias :use_ssl? :ssl?
113
+
104
114
  def setup
105
115
  @request = verb.new(@uri.request_uri, @headers)
106
116
  @request.basic_auth(@uri.user, @uri.password) if @uri.user && @uri.password
@@ -110,8 +120,19 @@ module SMG #:nodoc:
110
120
 
111
121
  def http
112
122
  http = @proxy ?
113
- Net::HTTP.new(@uri.host, @uri.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) :
114
- Net::HTTP.new(@uri.host, @uri.port)
123
+ Net::HTTP.new(@uri.host, @uri.inferred_port, @proxy.host, @proxy.inferred_port, @proxy.user, @proxy.password) :
124
+ Net::HTTP.new(@uri.host, @uri.inferred_port)
125
+
126
+ if ssl?
127
+ http.use_ssl = true
128
+ if @pem
129
+ http.cert = OpenSSL::X509::Certificate.new(@pem)
130
+ http.key = OpenSSL::PKey::RSA.new(@pem)
131
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
132
+ else
133
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
134
+ end
135
+ end
115
136
 
116
137
  return http unless @timeout
117
138
  http.open_timeout = @timeout
@@ -3,7 +3,6 @@ module SMG #:nodoc:
3
3
 
4
4
  attr_reader :elements, :nested, :attributes
5
5
  attr_reader :root
6
- attr_reader :parsed
7
6
 
8
7
  def initialize
9
8
  @elements = {}
@@ -45,8 +44,7 @@ module SMG #:nodoc:
45
44
 
46
45
  def handle_path(path)
47
46
  ret = normalize_path(path)
48
- ret.unshift(@root) if @root
49
- ret.flatten!
47
+ ret.unshift(*@root) if @root
50
48
  ret
51
49
  end
52
50
 
@@ -16,11 +16,7 @@ module SMG #:nodoc:
16
16
  @context = nil
17
17
 
18
18
  if options.key?(:context)
19
- raise ArgumentError, "+options[:context]+ should be an Array of Symbols" unless
20
- Array === options[:context] &&
21
- options[:context].all?{ |c| Symbol === c }
22
-
23
- @context = options[:context].compact
19
+ @context = Array(options[:context]).compact
24
20
  @context.uniq!
25
21
  @context = nil if @context.empty?
26
22
  end
@@ -11,7 +11,7 @@ module SMG #:nodoc:
11
11
 
12
12
  def [](key,value)
13
13
  return typecasts[key][value] if typecasts.key?(key)
14
- raise ArgumentError, "Can't typecast to #{key.inspect}"
14
+ raise ArgumentError, "Can't typecast #{value.class} into #{key.inspect}"
15
15
  end
16
16
 
17
17
  def key?(key)
@@ -21,20 +21,20 @@ module SMG #:nodoc:
21
21
  thing = Class === options[:class] ? mapping.attach_nested(tag,options) : mapping.attach_element(tag,options)
22
22
 
23
23
  if (instance_methods & [thing.accessor, thing.accessor.to_s]).empty?
24
- class_eval <<-CODE
24
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
25
25
  def #{thing.accessor}(value)
26
26
  @#{thing.name} ||= []
27
27
  @#{thing.name} << value
28
28
  end
29
- CODE
29
+ EOS
30
30
  end
31
31
 
32
32
  if (instance_methods & [thing.name, thing.name.to_s]).empty?
33
- class_eval <<-CODE
33
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
34
34
  def #{thing.name}
35
35
  @#{thing.name} ||= []
36
36
  end
37
- CODE
37
+ EOS
38
38
  end
39
39
 
40
40
  end
@@ -1,5 +1,5 @@
1
1
  module SMG
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.2'
3
3
  end
4
4
 
5
5
  # EOF
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smg
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 19
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 2
8
- - 1
9
- version: 0.2.1
9
+ - 2
10
+ version: 0.2.2
10
11
  platform: ruby
11
12
  authors:
12
13
  - SSDany
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-06-14 00:00:00 +04:00
18
+ date: 2010-07-16 00:00:00 +04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: nokogiri
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 9
27
30
  segments:
28
31
  - 1
29
32
  - 3
@@ -34,16 +37,34 @@ dependencies:
34
37
  name: addressable
35
38
  prerelease: false
36
39
  requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
37
41
  requirements:
38
42
  - - ">="
39
43
  - !ruby/object:Gem::Version
44
+ hash: 15
40
45
  segments:
41
46
  - 2
42
47
  - 1
43
- - 1
44
- version: 2.1.1
48
+ - 2
49
+ version: 2.1.2
45
50
  type: :runtime
46
51
  version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 31
61
+ segments:
62
+ - 1
63
+ - 2
64
+ - 0
65
+ version: 1.2.0
66
+ type: :development
67
+ version_requirements: *id003
47
68
  description: |
48
69
  XML to Object mapping library with simple declarative syntax.
49
70
  Supports 'contextual' parsing and conditions.
@@ -60,6 +81,7 @@ files:
60
81
  - README.rdoc
61
82
  - lib/smg/document.rb
62
83
  - lib/smg/http/exceptions.rb
84
+ - lib/smg/http/hooks.rb
63
85
  - lib/smg/http/request.rb
64
86
  - lib/smg/http.rb
65
87
  - lib/smg/mapping/element.rb
@@ -69,37 +91,6 @@ files:
69
91
  - lib/smg/resource.rb
70
92
  - lib/smg/version.rb
71
93
  - lib/smg.rb
72
- - examples/crazy.rb
73
- - examples/discogs/label.rb
74
- - examples/discogs/search.rb
75
- - examples/helper.rb
76
- - examples/plant.rb
77
- - examples/twitter.rb
78
- - examples/weather.rb
79
- - spec/collect_spec.rb
80
- - spec/context_spec.rb
81
- - spec/extract_spec.rb
82
- - spec/filtering_spec.rb
83
- - spec/fixtures/discogs/948224.xml
84
- - spec/fixtures/discogs/Enzyme+Records.xml
85
- - spec/fixtures/discogs/Genosha+Recordings.xml
86
- - spec/fixtures/discogs/Ophidian.xml
87
- - spec/fixtures/fake/malus.xml
88
- - spec/fixtures/fake/valve.xml
89
- - spec/fixtures/twitter/pipopolam.xml
90
- - spec/fixtures/yahoo.weather.com.xml
91
- - spec/http/request_spec.rb
92
- - spec/http/shared/automatic.rb
93
- - spec/http/shared/non_automatic.rb
94
- - spec/http/shared/redirectable.rb
95
- - spec/http_spec.rb
96
- - spec/lib/helpers/http_helpers.rb
97
- - spec/lib/matchers/instance_methods.rb
98
- - spec/mapping/element_spec.rb
99
- - spec/mapping/typecasts_spec.rb
100
- - spec/resource_spec.rb
101
- - spec/root_spec.rb
102
- - spec/spec_helper.rb
103
94
  has_rdoc: true
104
95
  homepage: http://github.com/SSDany/smg
105
96
  licenses: []
@@ -110,23 +101,27 @@ rdoc_options: []
110
101
  require_paths:
111
102
  - lib
112
103
  required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
113
105
  requirements:
114
106
  - - ">="
115
107
  - !ruby/object:Gem::Version
108
+ hash: 3
116
109
  segments:
117
110
  - 0
118
111
  version: "0"
119
112
  required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
120
114
  requirements:
121
115
  - - ">="
122
116
  - !ruby/object:Gem::Version
117
+ hash: 3
123
118
  segments:
124
119
  - 0
125
120
  version: "0"
126
121
  requirements: []
127
122
 
128
123
  rubyforge_project: smg
129
- rubygems_version: 1.3.6
124
+ rubygems_version: 1.3.7
130
125
  signing_key:
131
126
  specification_version: 3
132
127
  summary: Simple declaratibve XML parsing library. Backed by Nokogiri
@@ -1,37 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
-
3
- class A
4
- include SMG::Resource
5
- extract "a" , :as => :href , :at => :href
6
- extract "a" , :as => :content
7
- end
8
-
9
- class P
10
- include SMG::Resource
11
- extract "p" , :as => :raw
12
- collect "p/a" , :as => :links , :class => A
13
- end
14
-
15
- class Requirements
16
- include SMG::Resource
17
- root "div"
18
- extract "p" , :as => :raw
19
- extract "p" , :as => :p , :class => P
20
- collect "p/a" , :as => :links , :class => A
21
- end
22
-
23
- data = <<-HTML
24
- <div><p>Requirements: <a href="http://github.com/tenderlove/nokogiri">nokogiri</a> and <a href="http://github.com/sporkmonger/addressable">addressable</a>.</p></div>
25
- HTML
26
-
27
- parsed = Requirements.parse(data)
28
-
29
- puts parsed.raw #=> "Requirements: nokogiri and addressable."
30
- puts parsed.links.map { |a| a.href } #=> ["http://github.com/tenderlove/nokogiri", "http://github.com/sporkmonger/addressable"]
31
- puts parsed.links.map { |a| a.content } #=> ["nokogiri", "addressable"]
32
-
33
- puts parsed.p.raw #=> "Requirements: nokogiri and addressable."
34
- puts parsed.p.links.map { |a| a.href } #=> ["http://github.com/tenderlove/nokogiri", "http://github.com/sporkmonger/addressable"]
35
- puts parsed.p.links.map { |a| a.content } #=> ["nokogiri", "addressable"]
36
-
37
- # EOF