page_record 0.4.0 → 0.5.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/.rubocop.yml +13 -0
- data/CHANGES.md +5 -0
- data/Gemfile.lock +46 -14
- data/Guardfile +24 -0
- data/README.md +27 -1
- data/bin/autospec +16 -0
- data/bin/guard +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/lib/page_record/attribute_accessors.rb +57 -54
- data/lib/page_record/base.rb +27 -22
- data/lib/page_record/class_actions.rb +47 -50
- data/lib/page_record/class_methods.rb +252 -261
- data/lib/page_record/errors.rb +81 -82
- data/lib/page_record/finders.rb +129 -131
- data/lib/page_record/form_builder.rb +4 -4
- data/lib/page_record/formtastic.rb +20 -9
- data/lib/page_record/helpers.rb +192 -131
- data/lib/page_record/inspector.rb +36 -0
- data/lib/page_record/instance_actions.rb +44 -46
- data/lib/page_record/rspec.rb +1 -1
- data/lib/page_record/validation.rb +46 -0
- data/lib/page_record/version.rb +1 -1
- data/lib/page_record.rb +13 -15
- data/page_record.gemspec +4 -1
- data/spec/.rubocop.yml +4 -0
- data/spec/helpers_spec.rb +109 -100
- data/spec/inspector_spec.rb +70 -0
- data/spec/page_record_spec.rb +357 -388
- data/spec/spec_helper.rb +1 -3
- data/spec/support/shared_contexts.rb +24 -2
- data/spec/support/shared_examples.rb +41 -45
- data/spec/support/team.rb +4 -4
- data/spec/support/test_app.rb +10 -13
- data/spec/support/views/page-with-1-error.erb +5 -0
- data/spec/support/views/page-with-2-errors-on-different-attributes.erb +9 -0
- data/spec/support/views/page-with-2-errors-on-same-attribute.erb +6 -0
- data/spec/support/views/page-without-errors.erb +4 -0
- data/spec/validation_spec.rb +142 -0
- data/tmp/rspec_guard_result +1 -0
- metadata +80 -5
@@ -1,262 +1,253 @@
|
|
1
1
|
module PageRecord
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
private
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
raise MultipleRecords, "Found multiple HTML segments with selector #{selector} on page"
|
255
|
-
rescue Capybara::ElementNotFound
|
256
|
-
raise RecordNotFound, "#{selector} not found on page"
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
end
|
262
|
-
end
|
2
|
+
class Base
|
3
|
+
|
4
|
+
##
|
5
|
+
# Set's the default selector for this class
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# ```ruby
|
10
|
+
# class TeamPage < PageRecord::Base
|
11
|
+
# selector "#first-table"
|
12
|
+
# end
|
13
|
+
# ```
|
14
|
+
# @param new_selector [String] The default selector to be used for all finders
|
15
|
+
#
|
16
|
+
def self.selector( new_selector = nil)
|
17
|
+
@selector = new_selector if new_selector
|
18
|
+
@selector
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Set's the default filter for this class
|
23
|
+
#
|
24
|
+
#
|
25
|
+
# Example:
|
26
|
+
#
|
27
|
+
# ```ruby
|
28
|
+
# class TeamPage < PageRecord::Base
|
29
|
+
# filter ".champions-league"
|
30
|
+
# end
|
31
|
+
# ```
|
32
|
+
#
|
33
|
+
# @param new_filter [String] The default filter to be used for all finders
|
34
|
+
#
|
35
|
+
def self.filter( new_filter = nil)
|
36
|
+
@filter = new_filter if new_filter
|
37
|
+
@filter
|
38
|
+
end
|
39
|
+
|
40
|
+
# @private
|
41
|
+
def self.inherited(base)
|
42
|
+
base.class_eval do
|
43
|
+
set_type_name(base)
|
44
|
+
get_attribute_names
|
45
|
+
end
|
46
|
+
define_class_methods(base)
|
47
|
+
define_instance_methods(base)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Set's the page {PageRecord::Base} uses for all page operations.
|
52
|
+
# when no parameter is given or the parameter is nil, just return the current value
|
53
|
+
#
|
54
|
+
# @param new_page [Cabybara::Session] The Capybara page
|
55
|
+
#
|
56
|
+
# @return [Capybara::Session]
|
57
|
+
#
|
58
|
+
# rubocop:disable AvoidClassVars:
|
59
|
+
def self.page (new_page = nil)
|
60
|
+
new_page ? @@page = new_page : @@page
|
61
|
+
end
|
62
|
+
# rubocop:enable AvoidClassVars:
|
63
|
+
|
64
|
+
##
|
65
|
+
# Set's the page host class
|
66
|
+
#
|
67
|
+
# @param new_host_class an ActiveRecord like class
|
68
|
+
#
|
69
|
+
# @return [Class]
|
70
|
+
#
|
71
|
+
def self.host_class (new_host_class = nil)
|
72
|
+
if new_host_class
|
73
|
+
@host_class = new_host_class
|
74
|
+
@host_name = new_host_class.to_s
|
75
|
+
@type = @host_name.underscore
|
76
|
+
get_attribute_names
|
77
|
+
define_class_methods(self)
|
78
|
+
define_instance_methods(self)
|
79
|
+
end
|
80
|
+
@host_class
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Set's the default type for this class
|
85
|
+
#
|
86
|
+
# @param new_type [Symbol] The default type to be used for all finders. If type is nil just return the current type
|
87
|
+
#
|
88
|
+
# @return [Symbol] the type set.
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
#
|
92
|
+
# ```ruby
|
93
|
+
# class TopDivisonPage < PageRecord::Base
|
94
|
+
# type :team
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# TopDivisonPage.type # returns :team
|
98
|
+
# ```
|
99
|
+
#
|
100
|
+
#
|
101
|
+
def self.type(new_type = nil)
|
102
|
+
new_type ? @type = new_type : @type
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Set's the attributes this page recognises. This will override any types
|
107
|
+
# inherited from the host class. When you don't specify a parameter, or a nil parameter
|
108
|
+
# .attributes will return the current set of attributes
|
109
|
+
#
|
110
|
+
# @param new_attributes [Array] The attributes the page regognises
|
111
|
+
#
|
112
|
+
# @return [Array] returns the array of attributes the page recognises
|
113
|
+
# Example:
|
114
|
+
#
|
115
|
+
# ```ruby
|
116
|
+
# class TopDivisonPage < PageRecord::Base
|
117
|
+
# attributes [:name, :position, :ranking]
|
118
|
+
# end
|
119
|
+
# ```
|
120
|
+
#
|
121
|
+
#
|
122
|
+
def self.attributes(new_attributes = nil)
|
123
|
+
if new_attributes
|
124
|
+
undefine_class_methods(self)
|
125
|
+
undefine_instance_methods(self)
|
126
|
+
@attributes = new_attributes
|
127
|
+
define_class_methods(self)
|
128
|
+
define_instance_methods(self)
|
129
|
+
end
|
130
|
+
@attributes
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Add some new attributes to the already availabe attributes
|
135
|
+
#
|
136
|
+
# @param extra_attributes [Array] The additional attributes the page regognises
|
137
|
+
#
|
138
|
+
# Example:
|
139
|
+
#
|
140
|
+
# ```ruby
|
141
|
+
# class TopDivisonPage < PageRecord::Base
|
142
|
+
# add_attributes [:full_name, :address_line]
|
143
|
+
# end
|
144
|
+
# ```
|
145
|
+
#
|
146
|
+
#
|
147
|
+
def self.add_attributes(extra_attributes)
|
148
|
+
@attributes.concat(extra_attributes)
|
149
|
+
# TODO: check if we can optimise this to only add the new methods
|
150
|
+
define_class_methods(self)
|
151
|
+
define_instance_methods(self)
|
152
|
+
@attributes
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# @private
|
158
|
+
def self.set_type_name(base)
|
159
|
+
@host_name = base.to_s.gsub('Page', '')
|
160
|
+
@type = @host_name.underscore
|
161
|
+
@host_class = @host_name.constantize
|
162
|
+
rescue NameError
|
163
|
+
@host_name = ''
|
164
|
+
@host_class = ''
|
165
|
+
end
|
166
|
+
|
167
|
+
# @private
|
168
|
+
def self.get_attribute_names
|
169
|
+
@attributes = @host_class.attribute_names.clone
|
170
|
+
@attributes.delete('id') # id is a special case attribute
|
171
|
+
rescue NameError
|
172
|
+
@attributes = []
|
173
|
+
end
|
174
|
+
|
175
|
+
# @private
|
176
|
+
def self.define_accessor_methods(base)
|
177
|
+
base.instance_eval do
|
178
|
+
@attributes.each do | attribute |
|
179
|
+
define_method("#{attribute}?") do
|
180
|
+
read_attribute?(attribute)
|
181
|
+
end
|
182
|
+
define_method(attribute) do
|
183
|
+
read_attribute(attribute)
|
184
|
+
end
|
185
|
+
define_method("#{attribute}=") do | value|
|
186
|
+
write_attribute(attribute, value)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# @private
|
193
|
+
def self.undefine_accessor_methods(base)
|
194
|
+
base.instance_eval do
|
195
|
+
@attributes.each do | attribute |
|
196
|
+
remove_method("#{attribute}?")
|
197
|
+
remove_method(attribute)
|
198
|
+
remove_method("#{attribute}=")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# @private
|
204
|
+
def self.define_instance_methods(base)
|
205
|
+
define_accessor_methods(base)
|
206
|
+
end
|
207
|
+
|
208
|
+
# @private
|
209
|
+
def self.undefine_instance_methods(base)
|
210
|
+
undefine_accessor_methods(base)
|
211
|
+
end
|
212
|
+
|
213
|
+
# @private
|
214
|
+
def self.define_class_methods(base)
|
215
|
+
eigenclass = class << base; self; end
|
216
|
+
attributes = base.instance_variable_get('@attributes')
|
217
|
+
eigenclass.instance_eval do
|
218
|
+
attributes.each do | attribute|
|
219
|
+
define_method "find_by_#{attribute}" do | value, selector = '', filter = ''|
|
220
|
+
find_by_attribute(attribute, value, selector, filter)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# @private
|
227
|
+
def self.undefine_class_methods(base)
|
228
|
+
eigenclass = class << base; self; end
|
229
|
+
attributes = base.instance_variable_get('@attributes')
|
230
|
+
eigenclass.instance_eval do
|
231
|
+
attributes.each do | attribute|
|
232
|
+
remove_method "find_by_#{attribute}"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# @private
|
238
|
+
def self.context_for_selector(selector)
|
239
|
+
if selector.blank?
|
240
|
+
page
|
241
|
+
else
|
242
|
+
begin
|
243
|
+
page.find(selector).find(:xpath, '..')
|
244
|
+
rescue Capybara::Ambiguous
|
245
|
+
raise MultipleRecords, "Found multiple HTML segments with selector #{selector} on page"
|
246
|
+
rescue Capybara::ElementNotFound
|
247
|
+
raise RecordNotFound, "#{selector} not found on page"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|