aur.rb 0.1.0 → 0.2.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 +4 -4
- data/ChangeLog.md +136 -2
- data/LICENSE.txt +1 -1
- data/README.md +21 -1
- data/Rakefile +7 -10
- data/TODO.md +69 -0
- data/aur.rb.gemspec +1 -0
- data/bin/aur.rb +6 -0
- data/lib/aur.rb +43 -1853
- data/lib/aur/aur_rpc.rb +178 -0
- data/lib/aur/cli.rb +334 -0
- data/lib/aur/config.rb +330 -0
- data/lib/aur/db.rb +328 -0
- data/lib/aur/devtools.rb +258 -0
- data/lib/aur/helpers.rb +56 -0
- data/lib/aur/install_packages.rb +158 -0
- data/lib/aur/load_config.rb +5 -0
- data/lib/aur/makepkg.rb +445 -0
- data/lib/aur/no_load_config.rb +5 -0
- data/lib/aur/packages.rb +599 -0
- data/lib/aur/repos.rb +343 -0
- data/lib/aur/version.rb +1 -1
- data/lib/aur/versions.rb +186 -0
- metadata +22 -5
data/lib/aur/devtools.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'aur/config'
|
2
|
+
|
3
|
+
module Archlinux
|
4
|
+
class PacmanConf
|
5
|
+
def self.create(v, config: Archlinux.config)
|
6
|
+
v.is_a?(self) ? v : self.new(v, config: config) #pass empty keywords so that a Hash is seen as an argument and not a list of keywords
|
7
|
+
end
|
8
|
+
|
9
|
+
Archlinux.delegate_h(self, :@pacman_conf)
|
10
|
+
attr_accessor :pacman_conf, :config
|
11
|
+
|
12
|
+
def initialize(conf="/etc/pacman.conf", config: Archlinux.config, **keys)
|
13
|
+
@config=config
|
14
|
+
if conf.is_a?(String) or conf.is_a?(Pathname)
|
15
|
+
conf=parse(conf, **keys)
|
16
|
+
end
|
17
|
+
@pacman_conf=conf
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(content)
|
21
|
+
list=%i(HoldPkg IgnorePkg IgnoreGroup NoUpgrade NoExtract SigLevel LocalFileSigLevel RemoteFileSigLevel Usage Server)
|
22
|
+
mode=:options
|
23
|
+
config={options: {}, repos: {}}
|
24
|
+
content=content.each_line if content.is_a?(String)
|
25
|
+
content.each do |l|
|
26
|
+
if (m=l.match(/^\[([\w-]+)\]$/))
|
27
|
+
mode=m[1]
|
28
|
+
if mode == "options"
|
29
|
+
mode=:options
|
30
|
+
else
|
31
|
+
config[:repos][mode]||={}
|
32
|
+
end
|
33
|
+
else
|
34
|
+
key, value=l.split(' = ', 2)
|
35
|
+
key=key.to_sym
|
36
|
+
h = mode==:options ? config[:options] : config[:repos][mode]
|
37
|
+
if list.include?(key)
|
38
|
+
h[key]||=[]
|
39
|
+
h[key] << value
|
40
|
+
else
|
41
|
+
h[key]=value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
config
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse(file, raw: false, args: nil)
|
49
|
+
unless args
|
50
|
+
if raw
|
51
|
+
args=[:'pacconf', "--raw", "--config=#{file}"]
|
52
|
+
else
|
53
|
+
args=[:'pacman-conf', "--config=#{file}"]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
output=@config.launch(*args) do |*args|
|
57
|
+
SH.run_simple(*args, chomp: :lines)
|
58
|
+
end
|
59
|
+
self.class.parse(output)
|
60
|
+
end
|
61
|
+
|
62
|
+
def non_official_repos
|
63
|
+
repos=@pacman_conf[:repos]
|
64
|
+
repos.slice(*(repos.keys - %w(core extra community multilib testing community-testing multilib-testing)))
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
r=[]
|
69
|
+
print_values=lambda do |h, section|
|
70
|
+
r<<"[#{section}]" if section
|
71
|
+
h.each do |k,v|
|
72
|
+
case v
|
73
|
+
when nil
|
74
|
+
r << k
|
75
|
+
when Array
|
76
|
+
v.each { |vv| r << "#{k} = #{vv}" }
|
77
|
+
else
|
78
|
+
r << "#{k} = #{v}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
print_values.call(@pacman_conf[:options], "options")
|
83
|
+
@pacman_conf[:repos].each do |section, props|
|
84
|
+
print_values.call(props, section)
|
85
|
+
end
|
86
|
+
r.join("\n")+"\n"
|
87
|
+
end
|
88
|
+
|
89
|
+
def tempfile
|
90
|
+
SH::VirtualFile.new("pacman.conf", to_s)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Devtools
|
95
|
+
def self.create(v)
|
96
|
+
v.is_a?(self) ? v : self.new(v)
|
97
|
+
end
|
98
|
+
Archlinux.delegate_h(self, :@opts)
|
99
|
+
|
100
|
+
attr_accessor :config, :opts
|
101
|
+
def initialize(config: Archlinux.config, **opts)
|
102
|
+
@config=config
|
103
|
+
@opts=@config.opts.merge(opts)
|
104
|
+
# %i(pacman_conf makepkg_conf).each do |key|
|
105
|
+
# @opts[key]=@opts[key].tempfile.file if @opts[key].respond_to?(:tempfile)
|
106
|
+
# end
|
107
|
+
root=@opts.dig(:chroot, :root) and @opts[:chroot][:root]=Pathname.new(root)
|
108
|
+
add_binds
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_binds
|
112
|
+
require 'uri'
|
113
|
+
if (conf=@opts[:pacman_conf]).is_a?(PacmanConf)
|
114
|
+
conf[:repos].each do |_name, opts|
|
115
|
+
opts[:Server].each do |server|
|
116
|
+
server.match(%r!file://(.*)!) do |m|
|
117
|
+
@opts[:bind_ro]||=[]
|
118
|
+
@opts[:bind_ro] << URI.decode_www_form_component(m[1])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def files
|
126
|
+
%i(pacman_conf makepkg_conf).each do |key|
|
127
|
+
if @opts[key]
|
128
|
+
file=@opts[key]
|
129
|
+
file=file.tempfile.file if file.respond_to?(:tempfile)
|
130
|
+
yield key, file
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def pacman_config
|
136
|
+
Pacman.create(@opts[:pacman_conf])
|
137
|
+
end
|
138
|
+
|
139
|
+
def pacman(*args, default_opts: [], **opts, &b)
|
140
|
+
files do |key, file|
|
141
|
+
default_opts += ["--config", file] if key==:pacman_conf
|
142
|
+
end
|
143
|
+
opts[:method]||=:sh
|
144
|
+
@config.launch(:pacman, *args, default_opts: default_opts, **opts, &b)
|
145
|
+
end
|
146
|
+
|
147
|
+
def makepkg(*args, run: :sh, default_opts: [], **opts, &b)
|
148
|
+
files do |key, file|
|
149
|
+
# trick to pass options to pacman
|
150
|
+
args << "PACMAN_OPTS+=--config=#{file.shellescape}"
|
151
|
+
default_opts += ["--config", file] if key==:makepkg_conf
|
152
|
+
end
|
153
|
+
opts[:method]||=run
|
154
|
+
@config.launch(:makepkg, *args, default_opts: default_opts, **opts, &b)
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
def nspawn(*args, root: @opts.dig(:chroot,:root)+'root', default_opts: [], **opts, &b)
|
159
|
+
files do |key, file|
|
160
|
+
default_opts += ["-C", file] if key==:pacman_conf
|
161
|
+
default_opts += ["-M", file] if key==:makepkg_conf
|
162
|
+
end
|
163
|
+
if (binds_ro=@opts[:bind_ro])
|
164
|
+
binds_ro.each do |b|
|
165
|
+
args.unshift("--bind-ro=#{b}")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
if (binds_rw=@opts[:bind_rw])
|
169
|
+
binds_rw.each do |b|
|
170
|
+
args.unshift("--bind=#{b}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
args.unshift root
|
174
|
+
|
175
|
+
opts[:method]||=:sh
|
176
|
+
@config.launch(:'arch-nspawn', *args, default_opts: default_opts, **opts, &b)
|
177
|
+
end
|
178
|
+
|
179
|
+
# this takes the same options as nspawn
|
180
|
+
# => this creates or update a chroot
|
181
|
+
def mkarchroot(*args, nspawn: @opts.dig(:chroot, :update), default_opts: [], root: @opts.dig(:chroot, :root), **opts, &b)
|
182
|
+
files do |key, file|
|
183
|
+
default_opts += ["-C", file] if key==:pacman_conf
|
184
|
+
default_opts += ["-M", file] if key==:makepkg_conf
|
185
|
+
end
|
186
|
+
root.sudo_mkpath unless root.directory?
|
187
|
+
root=root+'root'
|
188
|
+
opts[:method]||=:sh
|
189
|
+
if (root+'.arch-chroot').file?
|
190
|
+
# Note that if nspawn is not called (and the chroot does not
|
191
|
+
# exist), then the passed pacman.conf will not be replace the one
|
192
|
+
# in the chroot. And when makechrootpkg calls nspawn, it does not
|
193
|
+
# transmit the -C/-M options. So even if we don't want to update,
|
194
|
+
# we should call a dummy bin like 'true'
|
195
|
+
if nspawn
|
196
|
+
return nspawn.call(root) if nspawn.is_a?(Proc)
|
197
|
+
nspawn=nspawn.shellsplit if nspawn.is_a?(String)
|
198
|
+
self.nspawn(*nspawn, root: root, **opts, &b)
|
199
|
+
end
|
200
|
+
else
|
201
|
+
@config.launch(:mkarchroot, root, *args, default_opts: default_opts, sudo: @config.sudo, **opts,&b)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def makechrootpkg(*args, default_opts: [], **opts, &b)
|
206
|
+
default_opts+=['-r', @opts.dig(:chroot, :root)]
|
207
|
+
if (binds_ro=@opts[:bind_ro])
|
208
|
+
binds_ro.each do |b|
|
209
|
+
default_opts += ["-D", b]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
if (binds_rw=@opts[:bind_rw])
|
213
|
+
binds_rw.each do |b|
|
214
|
+
default_opts += ["-d", b]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
opts[:method]||=:sh
|
218
|
+
|
219
|
+
#makechrootpkg calls itself with sudo --preserve-env=SOURCE_DATE_EPOCH,GNUPGHOME so it does not keep PKGDEST..., work around this by providing our own sudo
|
220
|
+
#@config.launch(:makechrootpkg, *args, default_opts: default_opts, sudo: @config.sudo('sudo --preserve-env=GNUPGHOME,PKGDEST,SOURCE_DATE_EPOCH'), **opts, &b)
|
221
|
+
## Update: this has been fixed in devtools-20190329
|
222
|
+
@config.launch(:makechrootpkg, *args, default_opts: default_opts, **opts, &b)
|
223
|
+
end
|
224
|
+
|
225
|
+
def tmp_pacman(conf, **opts)
|
226
|
+
PacmanConf.create(conf).tempfile.create(true) do |file|
|
227
|
+
pacman=lambda do |*args, **pac_opts, &b|
|
228
|
+
pac_opts[:method]||=:sh
|
229
|
+
@config.launch(:pacman, *args, default_opts: ["--config", file], **opts.merge(pac_opts), &b)
|
230
|
+
end
|
231
|
+
yield pacman, file
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def sync_db(*names, install: [], **pacman_opts)
|
236
|
+
conf=PacmanConf.create(@opts[:pacman_conf])
|
237
|
+
new_conf={options: conf[:options], repos: {}}
|
238
|
+
repos=conf[:repos]
|
239
|
+
names.each do |name|
|
240
|
+
if repos[name]
|
241
|
+
new_conf[:repos][name]=repos[name]
|
242
|
+
else
|
243
|
+
SH.logger.warn "sync_db: unknown repo #{name}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
tmp_pacman(new_conf) do |pacman, file|
|
247
|
+
if block_given?
|
248
|
+
return yield(pacman, file)
|
249
|
+
else
|
250
|
+
args=['-Syu']
|
251
|
+
args+=install
|
252
|
+
return pacman[*args, sudo: @config.sudo, **pacman_opts]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
data/lib/aur/helpers.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'dr/base/utils'
|
3
|
+
require 'dr/base/uri' # for DR::URIEscape.escape
|
4
|
+
require 'shell_helpers'
|
5
|
+
require 'dr/ruby_ext/core_ext'
|
6
|
+
|
7
|
+
module Archlinux
|
8
|
+
ArchlinuxError=Class.new(StandardError)
|
9
|
+
Utils=::DR::Utils
|
10
|
+
Pathname=::SH::Pathname
|
11
|
+
|
12
|
+
def self.delegate_h(klass, var)
|
13
|
+
# put in a Module so that they are easier to distinguish from the
|
14
|
+
# 'real' functions
|
15
|
+
m=Module.new do
|
16
|
+
extend(Forwardable)
|
17
|
+
methods=[:[], :[]=, :any?, :assoc, :clear, :compact, :compact!, :delete, :delete_if, :dig, :each, :each_key, :each_pair, :each_value, :empty?, :fetch, :fetch_values, :has_key?, :has_value?, :include?, :index, :invert, :keep_if, :key, :key?, :keys, :length, :member?, :merge, :merge!, :rassoc, :reject, :reject!, :select, :select!, :shift, :size, :slice, :store, :to_a, :to_h, :to_s, :transform_keys, :transform_keys!, :transform_values, :transform_values!, :update, :value?, :values, :values_at]
|
18
|
+
include(Enumerable)
|
19
|
+
def_delegators var, *methods
|
20
|
+
end
|
21
|
+
klass.include(m)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.add_to_hash(h, key, value)
|
25
|
+
case h[key]
|
26
|
+
when nil
|
27
|
+
h[key] = value
|
28
|
+
when Array
|
29
|
+
h[key] << value
|
30
|
+
else
|
31
|
+
h[key]=[h[key], value]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.create_class(klass, *parameters, **kw, &b)
|
36
|
+
klass=Archlinux.const_get(klass) if klass.is_a?(Symbol)
|
37
|
+
if klass.is_a?(Proc)
|
38
|
+
klass.call(*parameters, **kw, &b)
|
39
|
+
else
|
40
|
+
klass.new(*parameters, **kw, &b)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module CreateHelper
|
45
|
+
def create(v, config: Archlinux.config)
|
46
|
+
v.is_a?(self) ? v : self.new(v, config: config)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
## Not used: we modify Config#pretty_print directly
|
51
|
+
# module PPHelper
|
52
|
+
# def pretty_print_instance_variables
|
53
|
+
# instance_variables.reject {|n| n==:@config}.sort
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'aur/packages'
|
2
|
+
require 'aur/makepkg'
|
3
|
+
|
4
|
+
module Archlinux
|
5
|
+
# class that support installation (ie define install_method)
|
6
|
+
class InstallPackageList < PackageList
|
7
|
+
def initialize(*args, **opts)
|
8
|
+
super
|
9
|
+
@install_method=method(:install_method)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_makepkg_list(l)
|
13
|
+
MakepkgList.new(l.map {|p| Query.strip(p)}, config: @config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def install_method(l, **opts, &b)
|
17
|
+
# if we are used as a source, fall back to the upstream method
|
18
|
+
if @install_list&.respond_to?(:install_method)
|
19
|
+
@install_list.install_method(l, **opts, &b)
|
20
|
+
else
|
21
|
+
m=get_makepkg_list(l)
|
22
|
+
info=opts.delete(:pkgs_info)
|
23
|
+
if info
|
24
|
+
tops=info[:top_pkgs].keys
|
25
|
+
deps=info[:all_pkgs].keys-tops
|
26
|
+
#if we cache the makepkg, we need to update both deps and tops
|
27
|
+
#in case we did a previous install
|
28
|
+
# require 'pry'; binding.pry
|
29
|
+
deps.each { |dep| m[Query.strip(dep)]&.asdeps=true }
|
30
|
+
tops.each { |dep| m[Query.strip(dep)]&.asdeps=false }
|
31
|
+
end
|
32
|
+
m=b.call(m) if b #return false to prevent install
|
33
|
+
m.install(**opts) if m
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# cache aur queries
|
39
|
+
class AurCache < InstallPackageList
|
40
|
+
|
41
|
+
def initialize(*args, **kw)
|
42
|
+
super
|
43
|
+
@ext_query=method(:ext_query)
|
44
|
+
#@query_ignore=AurPackageList.official
|
45
|
+
if @config[:aur_url]==GlobalAurCache.config[:aur_url]
|
46
|
+
@klass=GlobalAurCache
|
47
|
+
else
|
48
|
+
@klass=AurQueryCustom.new(config: @config)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ext_query(*queries, **_opts)
|
53
|
+
pkgs=queries.map {|p| Query.strip(p)}
|
54
|
+
# in a query like foo>1000, even if foo exist and was queried,
|
55
|
+
# the query fails so it gets called in ext_query
|
56
|
+
# remove these packages
|
57
|
+
# TODO: do the same for a provides query
|
58
|
+
pkgs-=self.names
|
59
|
+
if pkgs.empty?
|
60
|
+
l=self.class.new([])
|
61
|
+
else
|
62
|
+
SH.logger.debug "! #{self.class}: Calling aur for infos on: #{pkgs.join(', ')}"
|
63
|
+
l=@klass.packages(*pkgs)
|
64
|
+
@query_ignore += pkgs - l.names #these don't exist in aur
|
65
|
+
end
|
66
|
+
r=l.resolve(*queries, ext_query: false, fallback: false)
|
67
|
+
return r, l
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# cache MakepkgList and download PKGBUILD dynamically
|
72
|
+
class MakepkgCache < InstallPackageList
|
73
|
+
attr_accessor :select_existing, :get_mode, :makepkg_list
|
74
|
+
def initialize(*args, get_mode: {}, **opts)
|
75
|
+
super(*args, **opts)
|
76
|
+
# puts "MakepkgCache called with options: #{[args, get_mode]}"
|
77
|
+
@select_existing=get_mode.delete(:existing)
|
78
|
+
@get_mode=get_mode
|
79
|
+
@ext_query=method(:ext_query)
|
80
|
+
@makepkg_list=MakepkgList.new([], config: @config)
|
81
|
+
#@query_ignore=AurPackageList.official
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_makepkg_list(l)
|
85
|
+
pkgs=l.map {|p| Query.strip(p)}
|
86
|
+
# use the cache
|
87
|
+
m=MakepkgList.new(pkgs.map {|pkg| @makepkg_list.key?(pkg) ? @makepkg_list[pkg] : pkg}, config: @config)
|
88
|
+
@makepkg_list.merge(m.l.values)
|
89
|
+
m
|
90
|
+
end
|
91
|
+
|
92
|
+
def ext_query(*queries, **opts)
|
93
|
+
m=get_makepkg_list(queries)
|
94
|
+
m.keep_if {|_k,v| v.exist?} if @select_existing
|
95
|
+
m.packages(get: @get_mode).as_ext_query(*queries, full_pkgs: true, **opts)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# combine Aur and Makepkg caches
|
100
|
+
class AurMakepkgCache < InstallPackageList
|
101
|
+
attr_accessor :aur_cache, :makepkg_cache
|
102
|
+
def initialize(*args, **opts)
|
103
|
+
super
|
104
|
+
@aur_cache = AurCache.new(**opts)
|
105
|
+
@makepkg_cache = MakepkgCache.new(get_mode: {update: true, clone: true, pkgver: true, view: true}, **opts)
|
106
|
+
@ext_query=method(:ext_query)
|
107
|
+
#@query_ignore=AurPackageList.official
|
108
|
+
end
|
109
|
+
|
110
|
+
def ext_query(*queries, **opts)
|
111
|
+
devel=queries.select do |query|
|
112
|
+
Query.strip(query)=~/(-git|-hg|-svn)$/
|
113
|
+
end
|
114
|
+
if @install_list_of
|
115
|
+
# we only want to check the pkgver of packages we already have; for
|
116
|
+
# the others the aur version is enough
|
117
|
+
devel=devel & @install_list_of.names
|
118
|
+
end
|
119
|
+
aur=queries-devel
|
120
|
+
r1, l1=@makepkg_cache.as_ext_query(*devel, **opts)
|
121
|
+
missing=devel-r1.keys
|
122
|
+
r2, l2=@aur_cache.as_ext_query(*(missing+aur), **opts)
|
123
|
+
return r1.merge(r2), l1.merge(l2)
|
124
|
+
end
|
125
|
+
|
126
|
+
def get_makepkg_list(l)
|
127
|
+
got=l.select {|pkg| @makepkg_cache.key?(pkg)}
|
128
|
+
got_m=@makepkg_cache.get_makepkg_list(got)
|
129
|
+
rest=@aur_cache.get_makepkg_list(l-got)
|
130
|
+
MakepkgList.new(l.map do |name|
|
131
|
+
strip=Query.strip(name)
|
132
|
+
got_m.key?(strip) ? got_m[strip] : rest[strip]
|
133
|
+
end, config: @config)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class AurPackageList < InstallPackageList
|
138
|
+
def self.official
|
139
|
+
@official||=%w(core extra community).map {|repo| Repo.new(repo).list(mode: :pacman)}.flatten.compact
|
140
|
+
end
|
141
|
+
|
142
|
+
def initialize(*args, **opts)
|
143
|
+
super
|
144
|
+
# @install_list=self.class.cache
|
145
|
+
@install_list=@config.install_list #AurMakepkgCache.new(**opts)
|
146
|
+
# TODO this won't work if we use several PackageList with the same
|
147
|
+
# cache at the same time
|
148
|
+
@install_list.install_list_of=self
|
149
|
+
@children_mode=%i(depends make_depends check_depends)
|
150
|
+
@install_method=method(:install_method)
|
151
|
+
@query_ignore=official
|
152
|
+
end
|
153
|
+
|
154
|
+
def official
|
155
|
+
self.class.official
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|