remote_resource 1.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 06c0f5f8b3a5ab36d0a2545bc7356d46652a9d09
4
+ data.tar.gz: e9c83ef1c274f63439dc1c117cb987086e4ba4ec
5
+ SHA512:
6
+ metadata.gz: 64d28b4b2a7a8e4080c64c475049a6c365bf08dbf3671a7a46c303f2c28fcc68f3ae26dc65ef165387c86aa8783d97324685636b98a47bed493df2bf966a5a0b
7
+ data.tar.gz: a982c4ad71b391459e9ab8128727017a01c1d66030c7b4a32073d2b605402d458b28a44d4d54913b3f1d7fc569d7f0d4f0e8ff137a7aa85bb9ab3145676e5173
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /.idea
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in remote_resource.gemspec
4
+ gemspec
5
+
6
+ gem 'activesupport', '~> 4'
7
+ gem 'activemodel', '~> 4'
8
+ gem 'nokogiri'
9
+ gem 'httparty'
10
+ gem 'chronic', require: false
11
+
12
+ group :test do
13
+ gem 'rspec', '~> 3.1.0'
14
+ gem 'webmock'
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 dizer
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # RemoteResource
2
+
3
+
4
+
5
+ ## Remote::Html::Model
6
+ Object interface over remote HTML (and JSON) resources.
7
+
8
+ ### Example
9
+
10
+ For example you have ```http://example.com/companies/a``` with this content:
11
+
12
+ ```html
13
+ <html>
14
+ <body>
15
+ <h1>Company A</h1>
16
+ <div class="desc">Lovely company</div>
17
+ </body>
18
+ </html>
19
+ ```
20
+
21
+ First, define Company model:
22
+
23
+ ```ruby
24
+ class Company < RemoteResource::Html::Model
25
+ attr_accessor :name, :description
26
+
27
+ mapping do |doc|
28
+ self.name = doc.c('h1')
29
+ self.description = doc.c('.desc')
30
+ end
31
+ end
32
+ ```
33
+
34
+ Now you can make requests:
35
+
36
+ ```ruby
37
+ company = Company.path('http://example.com/companies/a').find
38
+ company.name # => "Company A"
39
+ company.description # => "Lovely company"
40
+ ```
41
+
42
+ #### Document maps
43
+
44
+ ```mapping``` describes how to get attributes from html:
45
+
46
+ ```ruby
47
+ class Company < RemoteResource::Html::Model
48
+ attr_accessor :name, :description
49
+
50
+ mapping do |doc|
51
+ self.name = doc.c('h1')
52
+ self.description = doc.c('.desc')
53
+ end
54
+
55
+ mapping :fake_name do |doc|
56
+ self.name = 'Fake'
57
+ self.description = doc.c('.desc')
58
+ end
59
+ end
60
+
61
+ company = Company.path('http://example.com/companies/a').find(:fake_name)
62
+ company.name # => "Fake"
63
+ company.description # => "Lovely company"
64
+ ```
65
+
66
+ ##### Helpers
67
+
68
+ Inside document_map you can use special helpers:
69
+
70
+ ```ruby
71
+ c(selector) # Get content of html node by css selector
72
+ a(attribute, selector) # Get tag attribute by css selector
73
+ parse_date(string) # Get date, uses chronic gem to parse complex dates
74
+ ```
75
+
76
+ #### Collections
77
+
78
+ .find (and its alias .first) returns only first occurrence, meanwhile .all returns Array of all elements.
79
+ ```http://example.com/companies```:
80
+
81
+ ```html
82
+ <html>
83
+ <body>
84
+ <div class="company">
85
+ <h1>Company A</h1>
86
+ <div class="desc">Lovely company</div>
87
+ </div>
88
+ <div class="company">
89
+ <h1>Company B</h1>
90
+ <div class="desc">Worst company ever</div>
91
+ </div>
92
+ </body>
93
+ </html>
94
+ ```
95
+
96
+ Collection of companies:
97
+
98
+ ```ruby
99
+ companies = Company.path('http://example.com/companies').separate(css: '.company').all # => [<Company...>, <Company...>]
100
+ companies.first.name # => "Company A"
101
+ companies.last.name # => "Company B"
102
+ ```
103
+
104
+ ##### Pagination example
105
+ Pagination can be performed with multiple ways, for example:
106
+
107
+ ```ruby
108
+ class Company < RemoteResource::Html::Model
109
+ # ...
110
+ module RelationMethods
111
+ def page(n)
112
+ query(limit: 100, offset: n.to_i * 100)
113
+ end
114
+ end
115
+ end
116
+
117
+ Company.path('http://example.com/companies').page(0).all
118
+ # will perform request to http://example.com/companies?limit=100&offset=0
119
+ ```
120
+
121
+ ## Installation
122
+
123
+ Add this line to your application's Gemfile:
124
+
125
+ ```ruby
126
+ gem 'remote_resource'
127
+ ```
128
+
129
+ And then execute:
130
+
131
+ $ bundle
132
+
133
+ Or install it yourself as:
134
+
135
+ $ gem install remote_resource
136
+
137
+ ## Usage
138
+
139
+ TODO: Write usage instructions here
140
+
141
+ ## Contributing
142
+
143
+ 1. Fork it ( https://github.com/[my-github-username]/remote_resource/fork )
144
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
145
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
146
+ 4. Push to the branch (`git push origin my-new-feature`)
147
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,26 @@
1
+ module RemoteResource::Concerns::Attributes
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include ActiveModel::Serialization
6
+ end
7
+
8
+ def attributes
9
+ self.class.attributes
10
+ end
11
+
12
+ def to_localone_hash
13
+ serializable_hash
14
+ end
15
+
16
+ module ClassMethods
17
+ def attr_accessor(*vars)
18
+ @attributes = (@attributes || []) + vars
19
+ super
20
+ end
21
+
22
+ def attributes
23
+ Hash[(@attributes || []).map{|e| [e, nil]}]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module RemoteResource::Concerns::Mappings
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class << self
6
+ attr_accessor :mappings
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+ def mapping(name = :default, &block)
12
+ @mappings ||= {}
13
+ @mappings[name] = block
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ module RemoteResource::PartyQuery
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include HTTParty
6
+ end
7
+
8
+ def query(path, options={})
9
+ result = nil
10
+ query_options = default_options.deep_merge(options.compact)
11
+ time = Benchmark.ms do
12
+ response = case options.delete(:http_method)
13
+ when :post
14
+ post(path, query_options)
15
+ else
16
+ get(path, query_options)
17
+ end
18
+ raise ResponseException.new(response.code, response) if response.code != 200
19
+ result = response.parsed_response
20
+ end
21
+ result
22
+ ensure
23
+ logger.debug "(#{time.try(:round, 1) || 'Failed'} ms) #{path} opts: #{query_options.except(:logger, :log_level, :log_format)}" if logger
24
+ end
25
+
26
+ class ResponseException < StandardError
27
+ attr_accessor :code
28
+ def initialize(code, msg, &block)
29
+ @code = code
30
+ super(["HTTP code: #{code}", msg].join('. '), &block)
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def default_options
37
+ {
38
+ headers: {
39
+ 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36',
40
+ 'Accept-Language' => 'en-US,en;q=0.8',
41
+ },
42
+ logger: logger,
43
+ log_format: :apache
44
+ }
45
+ end
46
+
47
+ def get(path, options={}, &block)
48
+ self.class.get(path, options, &block)
49
+ end
50
+
51
+ def post(path, options={}, &block)
52
+ self.class.post(path, options, &block)
53
+ end
54
+
55
+ def logger
56
+ defined?(Rails) ? Rails.logger : nil
57
+ end
58
+
59
+ def debug?
60
+ false
61
+ end
62
+ end
@@ -0,0 +1,52 @@
1
+ module RemoteResource::Concerns::Relation
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ extend SingleForwardable
6
+ end
7
+
8
+ module ClassMethods
9
+ def relation
10
+ @relation ||= RemoteResource::Model::Relation.for_model(self)
11
+ end
12
+
13
+ def delegate_to_relation(*methods)
14
+ @delegated_to_relation ||= []
15
+ @delegated_to_relation += methods
16
+ single_delegate methods => :relation
17
+ end
18
+
19
+ def delegated_to_relation
20
+ collect_from_superclasses(:@delegated_to_relation)
21
+ end
22
+
23
+ def delegate_to_relation_merged(*methods)
24
+ @delegated_to_relation_merged ||= []
25
+ @delegated_to_relation_merged += methods
26
+ delegate_to_relation(*methods)
27
+ end
28
+
29
+ def delegated_to_relation_merged
30
+ collect_from_superclasses(:@delegated_to_relation_merged)
31
+ end
32
+
33
+ def delegate_from_relation(*methods)
34
+ @delegated_from_relation ||= []
35
+ @delegated_from_relation += methods
36
+ end
37
+
38
+ def delegated_from_relation
39
+ collect_from_superclasses(:@delegated_from_relation)
40
+ end
41
+
42
+ def collect_from_superclasses(variable_name)
43
+ methods = []
44
+ klass = self
45
+ while klass do
46
+ methods += Array.wrap(klass.instance_variable_get(variable_name))
47
+ klass = klass.superclass
48
+ end
49
+ methods
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module RemoteResource::Concerns
2
+
3
+ end
@@ -0,0 +1,28 @@
1
+ require 'remote_resource/concerns/party_query'
2
+
3
+ class RemoteResource::Connection
4
+ include RemoteResource::PartyQuery
5
+
6
+ def request(path, options={})
7
+ separate = options[:separate]
8
+
9
+ if options[:unwrap]
10
+ original_parser = self.class.parser
11
+ options[:parser] = Proc.new { |body|
12
+ body = options[:unwrap].call(body)
13
+ original_parser.call(body)
14
+ }
15
+ end
16
+
17
+ result = query(path, options)
18
+ Array.wrap(
19
+ if separate.try(:[], :json)
20
+ separate[:json].inject(result) { |r, p| r.try(:[], p.to_s) }
21
+ elsif separate.try(:[], :css)
22
+ result.css(separate[:css])
23
+ else
24
+ result
25
+ end
26
+ )
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ class RemoteResource::DocumentWrapper < SimpleDelegator
2
+ def css_content(selector)
3
+ selected = at_css(selector).try(:clone)
4
+ return unless selected
5
+ selected.css('br').each{ |br| br.replace "\n" }
6
+ selected.try(:content).to_s.strip
7
+ end
8
+
9
+ alias :c :css_content
10
+
11
+ def tag_attribute(attribute, selector)
12
+ at_css(selector).try(:[], attribute).to_s
13
+ end
14
+
15
+ alias :a :tag_attribute
16
+
17
+ def css_self_content(selector)
18
+ at_css(selector).try(:xpath, 'text()').to_s.strip
19
+ end
20
+
21
+ alias :c_self :css_self_content
22
+
23
+ def parse_date(date, options={})
24
+ if options.any? && options.delete(:chronic) != false
25
+ require 'chronic'
26
+ Chronic.parse(date.to_s, {guess: :begin, context: :past}.merge(options))
27
+ else
28
+ begin
29
+ Time.zone.parse(date.to_s)
30
+ rescue ArgumentError
31
+ nil
32
+ end
33
+ end
34
+ end
35
+
36
+ # abs_url('http://example.com', 'path?param=1')
37
+ # => 'http://example.com/path?param=1'
38
+ #
39
+ # abs_url('ftp://sub.domain.dev/other_path?other_param=2', 'path?param=1#anchor')
40
+ # => 'ftp://sub.domain.dev/path?param=1#anchor'
41
+ def abs_url(base, path)
42
+ abs = URI(base)
43
+ rel = URI(path)
44
+ rel.scheme = abs.scheme
45
+ rel.host = abs.host
46
+ rel.to_s
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ class RemoteResource::Html::Connection < RemoteResource::Connection
2
+ parser Proc.new { |body| Nokogiri::HTML(body) }
3
+ end
@@ -0,0 +1,7 @@
1
+ class RemoteResource::Html::Model < RemoteResource::Model
2
+ class << self
3
+ def connection
4
+ @connection ||= RemoteResource::Html::Connection.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ module RemoteResource::Html; end
@@ -0,0 +1,7 @@
1
+ class RemoteResource::Json::Connection < RemoteResource::Connection
2
+ parser Proc.new { |body| ActiveSupport::JSON.decode(body) }
3
+
4
+ def default_options
5
+ super.except(:logger)
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class RemoteResource::Json::Model < RemoteResource::Model
2
+ class << self
3
+ def connection
4
+ @connection ||= RemoteResource::Json::Connection.new
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,99 @@
1
+ class RemoteResource::Model::Relation
2
+
3
+ attr_reader :model_class
4
+ attr_accessor :attributes
5
+
6
+ def initialize(model_class, attributes={})
7
+ @model_class = model_class
8
+ self.attributes = attributes
9
+ end
10
+
11
+ def method_missing(meth, *args, &block)
12
+ if model_class.delegated_to_relation.try(:include?, meth)
13
+ if args.count > 0
14
+ arg = if model_class.delegated_to_relation_merged.try(:include?, meth)
15
+ (attributes[meth] || {}).merge(args.first)
16
+ else
17
+ args.first
18
+ end
19
+ self.class.for_model(model_class, attributes.merge(meth => arg))
20
+ else
21
+ attributes[meth]
22
+ end
23
+
24
+ elsif model_class.delegated_from_relation.try(:include?, meth)
25
+ options = args.extract_options!
26
+ model_class.send(meth, *args, options.merge(attributes), &block)
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def relation
33
+ self
34
+ end
35
+
36
+ def all(options={})
37
+ raise '`path` for query not specified' unless attributes[:path]
38
+ results = model_class.connection.request(
39
+ attributes[:path],
40
+ separate: attributes[:separate],
41
+ http_method: attributes[:via],
42
+ query: attributes[:query],
43
+ body: attributes[:body],
44
+ cookies: attributes[:cookies],
45
+ headers: attributes[:headers],
46
+ unwrap: attributes[:unwrap],
47
+ )
48
+ results.take(attributes[:limit] || results.count).map do |result_row|
49
+ build(result_row, options)
50
+ end
51
+ end
52
+
53
+ def first(options={})
54
+ limit(1).all(options).first
55
+ end
56
+
57
+ alias :find :first
58
+
59
+ def build(doc, options={}, &block)
60
+ model = model_class.new(url: attributes[:path])
61
+ document = RemoteResource::DocumentWrapper.new(doc)
62
+
63
+ mapping_name = options[:mapping] || :default
64
+ mapping = model_class.mappings[mapping_name]
65
+ raise "Mapping `#{mapping_name}` not found" unless mapping
66
+
67
+ model.instance_exec(document, &mapping)
68
+ instance_exec(model, &block) if block_given?
69
+ model
70
+ end
71
+
72
+ def on_all_pages(all_options={}, &block)
73
+ all_entities = []
74
+ begin
75
+ page = (page || -1) + 1
76
+ remaining_limit = attributes[:limit] ? attributes[:limit] - all_entities.count : nil
77
+ entities = block_given? ? instance_exec(page, &block) : limit(remaining_limit).page(page).all(all_options)
78
+ all_entities += entities if entities.any?
79
+ end while entities.any?
80
+ all_entities.compact
81
+ end
82
+
83
+ def on_pages(urls, all_options={})
84
+ on_all_pages do |n|
85
+ if urls[n]
86
+ path(urls[n]).all(all_options)
87
+ else
88
+ []
89
+ end
90
+ end
91
+ end
92
+
93
+ def self.for_model(model, attributes={})
94
+ rel = RemoteResource::Model::Relation.new(model, attributes)
95
+ rel.send(:extend, "#{model.name}::RelationMethods".constantize) if model.const_defined?(:RelationMethods)
96
+ rel
97
+ end
98
+
99
+ end
@@ -0,0 +1,35 @@
1
+ require 'active_support/all'
2
+ require 'active_model'
3
+ require 'httparty'
4
+ require 'nokogiri'
5
+
6
+ require 'remote_resource/connection'
7
+ require 'remote_resource/document_wrapper'
8
+
9
+ require 'remote_resource/concerns'
10
+ require 'remote_resource/concerns/attributes'
11
+ require 'remote_resource/concerns/mappings'
12
+ require 'remote_resource/concerns/relation'
13
+
14
+
15
+ class RemoteResource::Model
16
+ include ActiveModel::Model
17
+ include RemoteResource::Concerns::Attributes
18
+ include RemoteResource::Concerns::Relation
19
+ include RemoteResource::Concerns::Mappings
20
+
21
+ attr_accessor :url
22
+
23
+ delegate_to_relation :path, :separate, :via, :unwrap, :limit
24
+ delegate_to_relation_merged :query, :body, :cookies, :headers
25
+
26
+ def connection
27
+ self.class.connection
28
+ end
29
+
30
+ class << self
31
+ def connection
32
+ @connection ||= RemoteResource::Connection.new
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module RemoteResource
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'remote_resource/version'
2
+ require 'remote_resource/model'
3
+
4
+ require 'remote_resource/model/relation'
5
+
6
+ require 'remote_resource/html'
7
+ require 'remote_resource/html/connection'
8
+ require 'remote_resource/html/model'
9
+
10
+ module RemoteResource
11
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'remote_resource/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "remote_resource"
8
+ spec.version = RemoteResource::VERSION
9
+ spec.authors = ["dizer"]
10
+ spec.email = ["dizeru@gmail.com"]
11
+ spec.summary = %q{Access remote resources with OOP interface}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ module TestCase
4
+ class Company < RemoteResource::Html::Model
5
+ attr_accessor :name
6
+
7
+ mapping do |document|
8
+ self.name = document.c('h1')
9
+ end
10
+ end
11
+
12
+ class User < RemoteResource::Html::Model
13
+ attr_accessor :name, :link
14
+
15
+ mapping do |document|
16
+ self.name = document.c('a')
17
+ self.link = document.a('href', 'a')
18
+ end
19
+
20
+ mapping :only_name do |document|
21
+ self.name = document.c('a')
22
+ end
23
+ end
24
+ end
25
+
26
+ describe RemoteResource::Html::Model do
27
+
28
+ let(:url) { 'http://example.com/companies/a' }
29
+ let(:query) { TestCase::User.path(url).separate(css: 'li') }
30
+
31
+ before do
32
+ stub_request(:get, url).
33
+ to_return(status: 200, body: '
34
+ <body>
35
+ <h1>Company A</h1>
36
+ <ul class="users">
37
+ <li><a href="users/1">User 1</a></li>
38
+ <li><a href="users/2">User 2</a></li>
39
+ </ul>
40
+ </body>'
41
+ )
42
+ end
43
+
44
+ context '#all' do
45
+ subject { query.all }
46
+
47
+ it do
48
+ expect(subject).to all(be_instance_of(TestCase::User))
49
+ end
50
+
51
+ it do
52
+ expect(subject.count).to eq(2)
53
+ end
54
+
55
+ context 'instance' do
56
+ let(:user) { subject.first }
57
+ it do
58
+ expect(user.name).to eq('User 1')
59
+ expect(user.link).to eq('users/1')
60
+ end
61
+ end
62
+
63
+ context 'another mapping' do
64
+ it do
65
+ expect(query.all(mapping: :only_name)).to all(be_instance_of(TestCase::User))
66
+ expect(query.all(mapping: :only_name).count).to eq(2)
67
+ end
68
+
69
+ context 'instance' do
70
+ let(:user) { query.first(mapping: :only_name) }
71
+ it do
72
+ expect(user.name).to eq('User 1')
73
+ expect(user.link).to eq(nil)
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'limit' do
79
+ it do
80
+ expect(query.limit(nil).all.count).to eq(2)
81
+ expect(query.limit(0).all.count).to eq(0)
82
+ expect(query.limit(1).all.count).to eq(1)
83
+ expect(query.limit(2).all.count).to eq(2)
84
+ end
85
+ end
86
+ end
87
+
88
+ context '#first' do
89
+ subject { query.first }
90
+
91
+ it do
92
+ expect(subject).to be_instance_of(TestCase::User)
93
+ end
94
+
95
+ it do
96
+ expect(subject.name).to eq('User 1')
97
+ end
98
+ end
99
+
100
+ context 'relation' do
101
+ it do
102
+ expect(RemoteResource::Html::Model.path('a')).to be_instance_of(RemoteResource::Model::Relation)
103
+ end
104
+
105
+ it do
106
+ expect(RemoteResource::Html::Model.path('a').attributes).to eq(path: 'a')
107
+ expect(RemoteResource::Html::Model.path('a').path('b').attributes).to eq(path: 'b')
108
+ expect(RemoteResource::Html::Model.path('a').path).to eq('a')
109
+ end
110
+ end
111
+
112
+ context 'mappings' do
113
+ it do
114
+ expect(TestCase::User.mappings).to have_key(:default)
115
+ expect(TestCase::User.mappings).to have_key(:only_name)
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,6 @@
1
+ require 'webmock/rspec'
2
+ require File.expand_path('../../lib/remote_resource', __FILE__)
3
+
4
+ RSpec.configure do |config|
5
+ config.order = 'random'
6
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remote_resource
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - dizer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description:
42
+ email:
43
+ - dizeru@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - lib/remote_resource.rb
55
+ - lib/remote_resource/concerns.rb
56
+ - lib/remote_resource/concerns/attributes.rb
57
+ - lib/remote_resource/concerns/mappings.rb
58
+ - lib/remote_resource/concerns/party_query.rb
59
+ - lib/remote_resource/concerns/relation.rb
60
+ - lib/remote_resource/connection.rb
61
+ - lib/remote_resource/document_wrapper.rb
62
+ - lib/remote_resource/html.rb
63
+ - lib/remote_resource/html/connection.rb
64
+ - lib/remote_resource/html/model.rb
65
+ - lib/remote_resource/json/connection.rb
66
+ - lib/remote_resource/json/model.rb
67
+ - lib/remote_resource/model.rb
68
+ - lib/remote_resource/model/relation.rb
69
+ - lib/remote_resource/version.rb
70
+ - remote_resource.gemspec
71
+ - spec/model_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: ''
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Access remote resources with OOP interface
97
+ test_files:
98
+ - spec/model_spec.rb
99
+ - spec/spec_helper.rb
100
+ has_rdoc: