page_magic 1.2.6 → 2.0.0.alpha1
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 +5 -5
- data/.rubocop.yml +19 -3
- data/.simplecov +5 -3
- data/.zsh_config +6 -0
- data/Dockerfile +10 -0
- data/Gemfile +12 -12
- data/Gemfile.lock +133 -126
- data/Makefile +13 -0
- data/README.md +78 -14
- data/Rakefile +12 -2
- data/VERSION +1 -1
- data/circle.yml +4 -2
- data/lib/active_support/core_ext/object/to_query.rb +84 -0
- data/lib/page_magic.rb +7 -4
- data/lib/page_magic/class_methods.rb +4 -1
- data/lib/page_magic/driver.rb +3 -0
- data/lib/page_magic/drivers.rb +6 -5
- data/lib/page_magic/drivers/poltergeist.rb +2 -0
- data/lib/page_magic/drivers/rack_test.rb +3 -1
- data/lib/page_magic/drivers/selenium.rb +4 -2
- data/lib/page_magic/element.rb +19 -10
- data/lib/page_magic/element/locators.rb +4 -1
- data/lib/page_magic/element/query.rb +33 -33
- data/lib/page_magic/element/query_builder.rb +61 -0
- data/lib/page_magic/element/selector.rb +3 -0
- data/lib/page_magic/element/selector_methods.rb +3 -0
- data/lib/page_magic/element_context.rb +9 -12
- data/lib/page_magic/element_definition_builder.rb +10 -4
- data/lib/page_magic/elements.rb +51 -8
- data/lib/page_magic/exceptions.rb +4 -33
- data/lib/page_magic/instance_methods.rb +6 -1
- data/lib/page_magic/matcher.rb +12 -3
- data/lib/page_magic/session.rb +7 -5
- data/lib/page_magic/session_methods.rb +2 -0
- data/lib/page_magic/utils/string.rb +14 -0
- data/lib/page_magic/wait_methods.rb +3 -0
- data/lib/page_magic/watcher.rb +2 -0
- data/lib/page_magic/watchers.rb +4 -1
- data/page_magic.gemspec +19 -13
- data/spec/element_spec.rb +2 -0
- data/spec/lib/active_support/core_ext/object/to_query_test.rb +78 -0
- data/spec/page_magic/class_methods_spec.rb +2 -0
- data/spec/page_magic/driver_spec.rb +2 -0
- data/spec/page_magic/drivers/poltergeist_spec.rb +2 -0
- data/spec/page_magic/drivers/rack_test_spec.rb +2 -0
- data/spec/page_magic/drivers/selenium_spec.rb +2 -0
- data/spec/page_magic/drivers_spec.rb +2 -0
- data/spec/page_magic/element/locators_spec.rb +2 -0
- data/spec/page_magic/element/query_builder_spec.rb +110 -0
- data/spec/page_magic/element/query_spec.rb +35 -77
- data/spec/page_magic/element/selector_spec.rb +14 -7
- data/spec/page_magic/element_context_spec.rb +5 -10
- data/spec/page_magic/element_definition_builder_spec.rb +3 -1
- data/spec/page_magic/elements_spec.rb +19 -5
- data/spec/page_magic/instance_methods_spec.rb +2 -0
- data/spec/page_magic/matcher_spec.rb +3 -1
- data/spec/page_magic/session_methods_spec.rb +2 -0
- data/spec/page_magic/session_spec.rb +3 -4
- data/spec/page_magic/utils/string_spec.rb +36 -0
- data/spec/page_magic/wait_methods_spec.rb +5 -3
- data/spec/page_magic/watchers_spec.rb +2 -0
- data/spec/page_magic_spec.rb +5 -7
- data/spec/spec_helper.rb +3 -0
- data/spec/support/shared_contexts.rb +3 -1
- data/spec/support/shared_contexts/files_context.rb +2 -0
- data/spec/support/shared_contexts/nested_elements_html_context.rb +2 -0
- data/spec/support/shared_contexts/rack_application_context.rb +2 -0
- data/spec/support/shared_contexts/webapp_fixture_context.rb +3 -1
- data/spec/support/shared_examples.rb +2 -0
- data/spec/watcher_spec.rb +3 -0
- metadata +62 -40
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Bundler.require :test, :development
|
2
4
|
|
3
5
|
RuboCop::RakeTask.new
|
@@ -12,11 +14,19 @@ Jeweler::Tasks.new do |gem|
|
|
12
14
|
gem.license = 'ruby'
|
13
15
|
gem.summary = 'Framework for modeling and interacting with webpages'
|
14
16
|
gem.description = 'Framework for modeling and interacting with webpages which wraps capybara'
|
15
|
-
gem.email = 'info@
|
17
|
+
gem.email = 'info@lvl-up.uk'
|
16
18
|
gem.authors = ['Leon Davis']
|
17
19
|
gem.required_ruby_version = '>= 2.1'
|
18
20
|
end
|
19
21
|
|
20
22
|
Jeweler::RubygemsDotOrgTasks.new
|
21
23
|
|
22
|
-
|
24
|
+
require 'rake/testtask'
|
25
|
+
Rake::TestTask.new do |t|
|
26
|
+
t.libs << 'spec'
|
27
|
+
t.pattern = 'spec/**/*_test.rb'
|
28
|
+
t.warning = true
|
29
|
+
t.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
task default: [:spec, :test, 'rubocop:auto_correct']
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0.alpha1
|
data/circle.yml
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# Alias of <tt>to_s</tt>.
|
5
|
+
def to_param
|
6
|
+
to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
# Converts an object into a string suitable for use as a URL query string,
|
10
|
+
# using the given <tt>key</tt> as the param name.
|
11
|
+
def to_query(key)
|
12
|
+
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class NilClass
|
17
|
+
# Returns +self+.
|
18
|
+
def to_param
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TrueClass
|
24
|
+
# Returns +self+.
|
25
|
+
def to_param
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class FalseClass
|
31
|
+
# Returns +self+.
|
32
|
+
def to_param
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Array
|
38
|
+
# Calls <tt>to_param</tt> on all its elements and joins the result with
|
39
|
+
# slashes. This is used by <tt>url_for</tt> in Action Pack.
|
40
|
+
def to_param
|
41
|
+
collect(&:to_param).join '/'
|
42
|
+
end
|
43
|
+
|
44
|
+
# Converts an array into a string suitable for use as a URL query string,
|
45
|
+
# using the given +key+ as the param name.
|
46
|
+
#
|
47
|
+
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
|
48
|
+
def to_query(key)
|
49
|
+
prefix = "#{key}[]"
|
50
|
+
|
51
|
+
if empty?
|
52
|
+
nil.to_query(prefix)
|
53
|
+
else
|
54
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Hash
|
60
|
+
# Returns a string representation of the receiver suitable for use as a URL
|
61
|
+
# query string:
|
62
|
+
#
|
63
|
+
# {name: 'David', nationality: 'Danish'}.to_query
|
64
|
+
# # => "name=David&nationality=Danish"
|
65
|
+
#
|
66
|
+
# An optional namespace can be passed to enclose key names:
|
67
|
+
#
|
68
|
+
# {name: 'David', nationality: 'Danish'}.to_query('user')
|
69
|
+
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
|
70
|
+
#
|
71
|
+
# The string pairs "key=value" that conform the query string
|
72
|
+
# are sorted lexicographically in ascending order.
|
73
|
+
#
|
74
|
+
# This method is also aliased as +to_param+.
|
75
|
+
def to_query(namespace = nil)
|
76
|
+
collect do |key, value|
|
77
|
+
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
|
78
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
79
|
+
end
|
80
|
+
end.compact.sort! * '&'
|
81
|
+
end
|
82
|
+
|
83
|
+
alias to_param to_query
|
84
|
+
end
|
data/lib/page_magic.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$LOAD_PATH.unshift(File.dirname(__FILE__).to_s)
|
2
4
|
require 'capybara'
|
3
5
|
require 'page_magic/exceptions'
|
@@ -31,7 +33,8 @@ module PageMagic
|
|
31
33
|
def included(clazz)
|
32
34
|
clazz.class_eval do
|
33
35
|
include(InstanceMethods)
|
34
|
-
extend
|
36
|
+
extend ClassMethods
|
37
|
+
extend Elements
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
@@ -48,10 +51,10 @@ module PageMagic
|
|
48
51
|
# @param [Symbol] browser name of browser
|
49
52
|
# @param [String] url url to start the session on
|
50
53
|
# @param [Hash] options browser driver specific options
|
51
|
-
# @return [Session] configured
|
52
|
-
def session(application: nil, browser: :rack_test,
|
54
|
+
# @return [Session] configured session
|
55
|
+
def session(url: nil, application: nil, browser: :rack_test, options: {})
|
53
56
|
driver = drivers.find(browser)
|
54
|
-
raise
|
57
|
+
raise UnsupportedBrowserException unless driver
|
55
58
|
|
56
59
|
Capybara.register_driver browser do |app|
|
57
60
|
driver.build(app, browser: browser, options: options)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PageMagic
|
2
4
|
# module ClassMethods - contains class level methods for PageObjects
|
3
5
|
module ClassMethods
|
@@ -15,6 +17,7 @@ module PageMagic
|
|
15
17
|
# if one has not been set on the page object class it will return a default block that does nothing
|
16
18
|
def on_load(&block)
|
17
19
|
return @on_load || DEFAULT_ON_LOAD unless block
|
20
|
+
|
18
21
|
@on_load = block
|
19
22
|
end
|
20
23
|
|
@@ -34,7 +37,7 @@ module PageMagic
|
|
34
37
|
session_options = { browser: browser, options: options, url: url }
|
35
38
|
session_options[:application] = application if application
|
36
39
|
|
37
|
-
PageMagic.session(session_options).tap do |session|
|
40
|
+
PageMagic.session(**session_options).tap do |session|
|
38
41
|
session.visit(self, url: url)
|
39
42
|
end
|
40
43
|
end
|
data/lib/page_magic/driver.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PageMagic
|
2
4
|
# class Driver - instances are factories for drivers used by PageMagic
|
3
5
|
class Driver
|
4
6
|
attr_reader :supported_browsers, :handler
|
7
|
+
|
5
8
|
# Creates a driver definition
|
6
9
|
# @example
|
7
10
|
# Driver.new do |rack_application, options|
|
data/lib/page_magic/drivers.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'page_magic/driver'
|
4
|
+
require 'page_magic/utils/string'
|
2
5
|
module PageMagic
|
3
6
|
# class Drivers - creates an object that can be used to hold driver definitions
|
4
7
|
# These PageMagic gets the user's chosen driver from this object.
|
@@ -16,12 +19,10 @@ module PageMagic
|
|
16
19
|
# Loads drivers defined in files at the given path
|
17
20
|
# @param [String] path where the drivers are located
|
18
21
|
def load(path = "#{__dir__}/drivers")
|
19
|
-
|
20
|
-
|
21
|
-
Dir["#{path}/*.rb"].each do |driver_file|
|
22
|
+
Dir["#{path}/*.rb"].sort.each do |driver_file|
|
22
23
|
require driver_file
|
23
24
|
driver_name = File.basename(driver_file)[/(.*)\.rb$/, 1]
|
24
|
-
register self.class.const_get(
|
25
|
+
register self.class.const_get(Utils::String.classify(driver_name))
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
@@ -35,7 +36,7 @@ module PageMagic
|
|
35
36
|
# @param [Object] other subject of equality check
|
36
37
|
# @return [Boolean] true if the object is a match
|
37
38
|
def ==(other)
|
38
|
-
other.
|
39
|
+
other.respond_to?(:all) && all == other.all
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
PageMagic::Drivers::Selenium = PageMagic::Driver.new(:chrome, :firefox) do |app, options, browser|
|
2
|
-
require 'watir
|
3
|
-
Capybara::Selenium::Driver.new(app, options.dup.merge(browser: browser))
|
4
|
+
require 'watir'
|
5
|
+
Capybara::Selenium::Driver.new(app, **options.dup.merge(browser: browser))
|
4
6
|
end
|
data/lib/page_magic/element.rb
CHANGED
@@ -1,17 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
require 'page_magic/element/selector_methods'
|
3
5
|
require 'page_magic/element/locators'
|
4
6
|
require 'page_magic/element/selector'
|
5
|
-
require 'page_magic/element/
|
7
|
+
require 'page_magic/element/query_builder'
|
6
8
|
module PageMagic
|
7
9
|
# class Element - represents an element in a html page.
|
8
10
|
class Element
|
9
|
-
EVENT_TYPES = [
|
11
|
+
EVENT_TYPES = %i[set select select_option unselect_option click].freeze
|
10
12
|
DEFAULT_HOOK = proc {}.freeze
|
11
|
-
EVENT_NOT_SUPPORTED_MSG = '%s event not supported'
|
13
|
+
EVENT_NOT_SUPPORTED_MSG = '%s event not supported'
|
12
14
|
|
13
|
-
include
|
14
|
-
|
15
|
+
include Locators
|
16
|
+
include WaitMethods
|
17
|
+
include SessionMethods
|
18
|
+
include Watchers
|
19
|
+
include SelectorMethods
|
20
|
+
extend Forwardable
|
21
|
+
extend SelectorMethods
|
22
|
+
extend Elements
|
15
23
|
|
16
24
|
attr_reader :type, :name, :parent_element, :browser_element, :before_events, :after_events
|
17
25
|
|
@@ -24,7 +32,7 @@ module PageMagic
|
|
24
32
|
# @!method before_events
|
25
33
|
# If a block is passed in, it adds it to be run before an event is triggered on an element.
|
26
34
|
# @see .after_events
|
27
|
-
%i
|
35
|
+
%i[after_events before_events].each do |method|
|
28
36
|
define_method method do |&block|
|
29
37
|
instance_variable_name = "@#{method}".to_sym
|
30
38
|
instance_variable_value = instance_variable_get(instance_variable_name) || [DEFAULT_HOOK]
|
@@ -38,6 +46,7 @@ module PageMagic
|
|
38
46
|
# @return [Element]
|
39
47
|
def parent_element(page_element = nil)
|
40
48
|
return @parent_page_element unless page_element
|
49
|
+
|
41
50
|
@parent_page_element = page_element
|
42
51
|
end
|
43
52
|
|
@@ -78,9 +87,7 @@ module PageMagic
|
|
78
87
|
# @raise [NotSupportedException] if the wrapped Capybara element does not support the method
|
79
88
|
EVENT_TYPES.each do |method|
|
80
89
|
define_method method do |*args|
|
81
|
-
unless browser_element.respond_to?(method)
|
82
|
-
raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method
|
83
|
-
end
|
90
|
+
raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method unless browser_element.respond_to?(method)
|
84
91
|
|
85
92
|
browser_element.send(method, *args)
|
86
93
|
end
|
@@ -91,7 +98,8 @@ module PageMagic
|
|
91
98
|
rescue ElementMissingException
|
92
99
|
begin
|
93
100
|
return browser_element.send(method, *args, &block) if browser_element.respond_to?(method)
|
94
|
-
|
101
|
+
|
102
|
+
parent_element.send(method, *args, &block)
|
95
103
|
rescue NoMethodError
|
96
104
|
super
|
97
105
|
end
|
@@ -131,6 +139,7 @@ module PageMagic
|
|
131
139
|
def wrap_events(raw_element)
|
132
140
|
EVENT_TYPES.each do |action_method|
|
133
141
|
next unless raw_element.respond_to?(action_method)
|
142
|
+
|
134
143
|
apply_hooks(raw_element: raw_element,
|
135
144
|
capybara_method: action_method,
|
136
145
|
before_events: before_events,
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PageMagic
|
2
4
|
# for the benefit of pull review :@
|
3
5
|
class Element
|
4
6
|
# contains method for finding element definitions
|
5
7
|
module Locators
|
6
8
|
# message used when raising {ElementMissingException} from methods within this module
|
7
|
-
ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'
|
9
|
+
ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'
|
8
10
|
|
9
11
|
# find an element definition based on its name
|
10
12
|
# @param [Symbol] name name of the element
|
@@ -13,6 +15,7 @@ module PageMagic
|
|
13
15
|
def element_by_name(name, *args)
|
14
16
|
defintion = element_definitions[name]
|
15
17
|
raise ElementMissingException, (ELEMENT_NOT_DEFINED_MSG % name) unless defintion
|
18
|
+
|
16
19
|
defintion.call(self, *args)
|
17
20
|
end
|
18
21
|
|
@@ -1,45 +1,45 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module PageMagic
|
3
4
|
class Element
|
4
|
-
# class Query -
|
5
|
-
# - requirements on element type
|
6
|
-
# - selection criteria, modeled through the Selector class
|
7
|
-
# - options
|
5
|
+
# class Query - executes query on capybara driver
|
8
6
|
class Query
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def find(type)
|
14
|
-
query = constants.find { |constant| constant.to_s.casecmp(type.to_s).zero? }
|
15
|
-
return ELEMENT unless query
|
16
|
-
const_get(query)
|
17
|
-
end
|
18
|
-
end
|
7
|
+
# Message template for execptions raised as a result of calling method_missing
|
8
|
+
ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'
|
9
|
+
|
10
|
+
attr_reader :args, :multiple_results
|
19
11
|
|
20
|
-
|
12
|
+
alias multiple_results? multiple_results
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
@
|
14
|
+
def initialize(args, multiple_results: false)
|
15
|
+
@args = args
|
16
|
+
@multiple_results = multiple_results
|
25
17
|
end
|
26
18
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
19
|
+
def execute(capybara_element)
|
20
|
+
if multiple_results
|
21
|
+
capybara_element.all(*args).to_a.tap do |result|
|
22
|
+
raise Capybara::ElementNotFound if result.empty?
|
23
|
+
end
|
24
|
+
else
|
25
|
+
# TODO - make sure there is a test around this.
|
26
|
+
if args.last.is_a?(Hash)
|
27
|
+
capybara_element.find(*args[0...-1], **args.last)
|
28
|
+
else
|
29
|
+
capybara_element.find(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
rescue Capybara::Ambiguous => e
|
35
|
+
raise AmbiguousQueryException, e.message
|
36
|
+
rescue Capybara::ElementNotFound => e
|
37
|
+
raise ElementMissingException, e.message
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
BUTTON = Query.new(:button)
|
40
|
+
def ==(other)
|
41
|
+
other.respond_to?(:args) && args == other.args
|
42
|
+
end
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'page_magic/element/query'
|
4
|
+
module PageMagic
|
5
|
+
class Element
|
6
|
+
# class QueryBuilder - builds query to be executed on capybara driver, queries can include:
|
7
|
+
# - requirements on element type
|
8
|
+
# - selection criteria, modeled through the Selector class
|
9
|
+
# - options
|
10
|
+
class QueryBuilder
|
11
|
+
class << self
|
12
|
+
# Find a query using it's name
|
13
|
+
# @param [Symbol] type the name of the required query in snakecase format
|
14
|
+
# @return [QueryBuilder] returns the predefined query with the given name
|
15
|
+
def find(type)
|
16
|
+
query = constants.find { |constant| constant.to_s.casecmp(type.to_s).zero? }
|
17
|
+
return ELEMENT unless query
|
18
|
+
|
19
|
+
const_get(query)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :type
|
24
|
+
|
25
|
+
# @param type -
|
26
|
+
def initialize(type = nil)
|
27
|
+
@type = type
|
28
|
+
end
|
29
|
+
|
30
|
+
# Build query parameters for Capybara's find method
|
31
|
+
# @param [Hash] locator the location method e.g. text: 'button text'
|
32
|
+
# @param [Hash] capybara_options additional options to be provided to Capybara. e.g. count: 3
|
33
|
+
# @return [Array] list of compatible capybara query parameters.
|
34
|
+
def build(locator, capybara_options = {}, multiple_results: false)
|
35
|
+
args = [].tap do |array|
|
36
|
+
selector = Selector.find(locator.keys.first)
|
37
|
+
array << selector.build(type, locator.values.first)
|
38
|
+
array << capybara_options unless capybara_options.empty?
|
39
|
+
end.flatten
|
40
|
+
|
41
|
+
Query.new(args, multiple_results: multiple_results)
|
42
|
+
end
|
43
|
+
|
44
|
+
ELEMENT = QueryBuilder.new
|
45
|
+
TEXT_FIELD =
|
46
|
+
CHECKBOX =
|
47
|
+
SELECT_LIST =
|
48
|
+
RADIO =
|
49
|
+
TEXTAREA =
|
50
|
+
FIELD =
|
51
|
+
FILE_FIELD =
|
52
|
+
FILLABLE_FIELD =
|
53
|
+
RADIO_BUTTON =
|
54
|
+
SELECT = QueryBuilder.new(:field)
|
55
|
+
|
56
|
+
LINK = QueryBuilder.new(:link)
|
57
|
+
LABEL = QueryBuilder.new(:label)
|
58
|
+
BUTTON = QueryBuilder.new(:button)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|