page_record 0.0.1

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.
@@ -0,0 +1,88 @@
1
+ module PageRecord
2
+
3
+
4
+ ##
5
+ # This {::Exception} is raised when the specified record is not found
6
+ # on the page. Check your selector, filter and HTML code for details.
7
+ #
8
+ # ```html
9
+ # <div data-team-id='10'>
10
+ # <div data-attribute-for='name'>Ajax</div>
11
+ # </div>
12
+ # ```
13
+ # When the following code is executed, the {RecordNotFound} exception is raised.
14
+ #
15
+ # ```ruby
16
+ # TeamPage.find(11)
17
+ #```
18
+ #
19
+ class RecordNotFound < Exception
20
+ end
21
+
22
+ ##
23
+ # This {::Exception} is raised when the specfied attribute is not found
24
+ # on the page. Check your selector, filter and HTML code for details.
25
+ #
26
+ # ```html
27
+ # <div data-team-id='10'>
28
+ # <div data-attribute-for='name'>Ajax</div>
29
+ # </div>
30
+ # ```
31
+ # When the following code is executed, the {AttributeNotFound} exception is raised.
32
+ #
33
+ # ```ruby
34
+ # TeamPage.find(10).ranking
35
+ #```
36
+ #
37
+ class AttributeNotFound < Exception
38
+ end
39
+
40
+ ##
41
+ # This {::Exception} is raised when you have not set the page variable
42
+ # of the class. Check {PageRecord::PageRecord.page} for details
43
+ #
44
+ class PageNotSet < Exception
45
+ end
46
+
47
+ ##
48
+ # This {::Exception} is raised when the page contains multiple instances
49
+ # of the specfied record type. Use a selector to narrow the search.
50
+ #
51
+ # ```html
52
+ # <div id='first-table' data-team-id='10'>
53
+ # <div data-attribute-for='name'>Ajax</div>
54
+ # </div>
55
+ # <div id='second-table' data-team-id='10'>
56
+ # <div data-attribute-for='name'>Ajax</div>
57
+ # </div>
58
+ # ```
59
+ # When the following code is executed, the {MultipleRecords} exception is raised.
60
+ #
61
+ # ```ruby
62
+ # TeamPage.find(10)
63
+ #```
64
+ #
65
+ # To fix this, use the `#first-table` in the selector
66
+ #
67
+ # ```ruby
68
+ # TeamPage.find(10, '#first-table')
69
+ #```
70
+ #
71
+ class MultipleRecords < Exception
72
+ end
73
+
74
+ # This {::Exception} is raised when you try to set a non input field.
75
+ #
76
+ # ```ruby
77
+ # <div data-team-id=1>
78
+ # <div data-attribute-for='name'>PSV</div>
79
+ # </div>
80
+ # ```
81
+ # When the following code is executed, the {NotInputField} exception is raised.
82
+ #
83
+ # ```ruby
84
+ # team_on_page.name = 'Ajax'
85
+ # ```
86
+ class NotInputField < Exception
87
+ end
88
+ end
@@ -0,0 +1,135 @@
1
+ module PageRecord
2
+ ##
3
+ # PageRecord is a specific sort of {http://assertselenium.com/automation-design-practices/page-object-pattern/ PageObject pattern}. Where a "normal"
4
+ # {http://assertselenium.com/automation-design-practices/page-object-pattern/ PageObject} tries to make the page accessible with business like functions.
5
+ # We have taken a different approach. We've noticed that a lot of WebPage are
6
+ # mainly luxury CRUD pages. This means that almost every page shows one or more
7
+ # records of a certain type and has the ability to create, read update and delete
8
+ # one or more records. Sounds familiar? Yes, it is the same as an ActiveRecord
9
+ # pattern. So we tried to make accessing a page as close as possible to accessing
10
+ # an ActiveRecord.
11
+ #
12
+ # To make this work, however, we need to add extra information to the HTML page.
13
+ # With HTML5, , you can do this easily. HTML-5 supports the data- attributes on any
14
+ # tag. We use these tags to identify the records on the page.
15
+ #
16
+ #
17
+ class PageRecord
18
+ ##
19
+ #
20
+ # Searches the page and returns an {::Array} of {PageRecord::PageRecord} of instances of
21
+ # that match the selector and the filter. See {file:README.md#markup markup} for more
22
+ # details about formatting the page.
23
+ #
24
+ # example:
25
+ #
26
+ # ```ruby
27
+ # TeamPage.all
28
+ # ```
29
+ #
30
+ # returns all records on the page
31
+ #
32
+ # @param selector [String] selector to use for searching the table on the page
33
+ # @param filter [String] filter to use on the records on the page
34
+ #
35
+ # @return [Array Pagerecord] The Array containing instances of [PageRecord::PageRecord]
36
+ # with records that fit the selector and the filter
37
+ #
38
+ # @raise [MultipleRecords] if the page contains more then on set of records
39
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
40
+ #
41
+ def self.all(selector = nil, filter = nil)
42
+ selector ||= self.instance_variable_get('@selector')
43
+ filter ||= self.instance_variable_get('@filter')
44
+ records = []
45
+ context = context_for_selector(selector)
46
+ context.all("[data-#{@type}-id]#{filter}").each do | record|
47
+ id = record["data-#{@type}-id"]
48
+ records << self.new(id, selector)
49
+ end
50
+ records
51
+ end
52
+
53
+ ##
54
+ #
55
+ # Searches the page and returns an instance of {PageRecord::PageRecord} of instances of
56
+ # that matches the given id, selector and the filter. See {file:README.md#markup markup} for more
57
+ # details about formatting the page.
58
+ #
59
+ # example:
60
+ #
61
+ # ```ruby
62
+ # TeamPage.find(1)
63
+ # ```
64
+ #
65
+ # returns the record with id
66
+ #
67
+ # When you don't specify an id, `find` returns the only record on the page.
68
+ # If you have more than one record on the page, `find` raises {MultipleRecords}.
69
+ #
70
+ # example:
71
+ #
72
+ # ```ruby
73
+ # TeamPage.find()
74
+ # ```
75
+ #
76
+ # @param selector [String] selector to use for searching the table on the page
77
+ # @param filter [String] filter to use on the records on the page
78
+ #
79
+ # @return [Pagerecord] An instance of [PageRecord::PageRecord]
80
+ #
81
+ # @raise [MultipleRecords] if the page contains more then on set of records
82
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
83
+ #
84
+ def self.find(id=nil, selector = nil, filter= nil)
85
+ selector ||= self.instance_variable_get('@selector')
86
+ filter ||= self.instance_variable_get('@filter')
87
+ self.new(id, selector, filter)
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Searches the page and returns an instance of {PageRecord::PageRecord} of instances of
93
+ # that matches the given attribute. See {file:README.md#markup markup} for more
94
+ # details about formatting the page.
95
+ #
96
+ # Although you can call this yourself, {PageRecord::PageRecord} uses this method for defining a
97
+ # finder for all attributes when you define your page class, {PageRecord::PageRecord}
98
+ # See {PageRecord::PageRecord.attributes} for more details.
99
+ #
100
+ # example:
101
+ #
102
+ # ```ruby
103
+ # TeamPage.find_by_name('Ajax')
104
+ # ```
105
+ #
106
+ # returns the record where the name is set to Ajax
107
+ #
108
+ # @param attribute [String] The attribute name
109
+ # @param value [String] The value to search for
110
+ # @param selector [String] selector to use for searching the table on the page
111
+ # @param filter [String] filter to use on the records on the page
112
+ #
113
+ # @return [Pagerecord] An instance of [PageRecord::PageRecord].
114
+ #
115
+ # @raise [MultipleRecords] if the page contains more then on set of records
116
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
117
+ #
118
+ def self.find_by_attribute(attribute, value, selector, filter)
119
+ begin
120
+ selector ||= self.instance_variable_get('@selector')
121
+ filter ||= self.instance_variable_get('@filter')
122
+
123
+ context = self.context_for_selector(selector)
124
+ record = context.find("[data-#{@type}-id]#{filter} > [data-attribute-for='#{attribute}']", :text => value)
125
+ parent = record.find(:xpath, "..")
126
+ id = parent["data-#{@type}-id"]
127
+ self.new(id, selector)
128
+ rescue Capybara::Ambiguous
129
+ raise MultipleRecords, "Found multiple #{@type} record with #{attribute} #{value} on page"
130
+ rescue Capybara::ElementNotFound
131
+ raise RecordNotFound, "#{@type} record with #{attribute} #{value} not found on page"
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,52 @@
1
+ module PageRecord
2
+ class PageRecord
3
+
4
+ ##
5
+ # This is the implementation of the record action routine. It has two variants.
6
+ # it has a `?` variant and a `normal` variant.
7
+ #
8
+ # normal variant:
9
+ # It checks the page for a data-action-for='action' tag somewhere on the page.
10
+ # If it finds it, it clicks it.
11
+ #
12
+ # `?` variant:
13
+ # It checks the page for a data-action-for='action' tag somewhere on the page.
14
+ # If it finds it, returns the Capybara element.
15
+ #
16
+ # @param action [Symbol] this is the name of the action
17
+ #
18
+ # @return [Capybara::Result]
19
+ #
20
+ # @raise [PageRecord::MultipleRecords] when there are more actions with
21
+ # this name on the page
22
+ #
23
+ def method_missing(action)
24
+ raw_action = /(.*)\?/.match(action)
25
+ begin
26
+ if raw_action
27
+ action_for?(raw_action[1])
28
+ else
29
+ action_for(action)
30
+ end
31
+ rescue Capybara::ElementNotFound
32
+ super
33
+ end
34
+ end
35
+
36
+
37
+ private
38
+
39
+ # @private
40
+ def action_for(action)
41
+ element = action_for?(action)
42
+ element.click
43
+ element
44
+ end
45
+
46
+ # @private
47
+ def action_for?(action)
48
+ @record.find("[data-action-for='#{action}']")
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,29 @@
1
+
2
+ module PageRecord
3
+
4
+ class PageRecord
5
+
6
+ attr_reader :id
7
+ alias :id? :id
8
+
9
+ def initialize(id=nil, selector=nil, filter=nil)
10
+ selector ||= self.instance_variable_get('@selector')
11
+ filter ||= self.instance_variable_get('@filter')
12
+ @page = self.class.page
13
+ # raise PageNotSet, "page variable not set" unless @page
14
+ @type = self.class.instance_variable_get('@type')
15
+ @id = id.to_s
16
+ id_text = @id.blank? ? "" : "='#{@id}'"
17
+ begin
18
+ context = self.class.context_for_selector(selector)
19
+ @record = context.find("[data-#{@type}-id#{id_text}]#{filter}")
20
+ @id = @record["data-#{@type}-id"] if @id.blank?
21
+ rescue Capybara::Ambiguous
22
+ raise MultipleRecords, "Found multiple #{@type} record with id #{@id} on page"
23
+ rescue Capybara::ElementNotFound
24
+ raise RecordNotFound, "#{@type} record with id #{@id} not found on page"
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ require 'page_record'
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:each) do
5
+ PageRecord::PageRecord.page = session
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module PageRecord
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support/core_ext'
2
+ require "page_record/version"
3
+ require "page_record/page_record"
4
+ require "page_record/finders"
5
+ require "page_record/instance_actions"
6
+ require "page_record/attribute_accessors"
7
+ require "page_record/class_actions"
8
+ require "page_record/class_methods"
9
+ require "page_record/errors"
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'page_record/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "page_record"
8
+ spec.version = PageRecord::VERSION
9
+ spec.authors = ["Bert Hajee"]
10
+ spec.email = ["hajee@moretIA.com"]
11
+ spec.description = %q{ActiveRecord like reading from specialy formatted HTML-page}
12
+ spec.summary = %q{Using some specialy formatted 'data-...' tags you can read records from HTML pages like an ActiveRecord page}
13
+ spec.homepage = "https://github.com/appdrones/page_record"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "sinatra"
25
+ # spec.add_development_dependency "debugger"
26
+
27
+ spec.add_dependency "capybara" , '~>2.1.0'
28
+ spec.add_dependency "activesupport"
29
+ end