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