spqr 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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +202 -0
- data/README.rdoc +28 -0
- data/Rakefile +105 -0
- data/TODO +33 -0
- data/VERSION +1 -0
- data/bin/spqr-gen.rb +60 -0
- data/examples/codegen-schema.xml +7 -0
- data/examples/codegen/EchoAgent.rb +33 -0
- data/examples/hello.rb +44 -0
- data/examples/logservice.rb +90 -0
- data/lib/rhubarb/rhubarb.rb +504 -0
- data/lib/spqr/app.rb +299 -0
- data/lib/spqr/codegen.rb +434 -0
- data/lib/spqr/constants.rb +64 -0
- data/lib/spqr/manageable.rb +222 -0
- data/lib/spqr/spqr.rb +16 -0
- data/lib/spqr/utils.rb +88 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/spqr_spec.rb +5 -0
- data/spqr.spec.in +95 -0
- data/test/helper.rb +11 -0
- data/test/test_rhubarb.rb +608 -0
- data/test/test_spqr.rb +7 -0
- metadata +97 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Constants for SQPR
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
6
|
+
#
|
7
|
+
# Author: William Benton (willb@redhat.com)
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
|
15
|
+
module SPQR
|
16
|
+
|
17
|
+
module XmlConstants
|
18
|
+
Type = {
|
19
|
+
'absTime' => 'Qmf::TYPE_ABSTIME',
|
20
|
+
'array' => 'Qmf::TYPE_ARRAY', # XXX: is this right?
|
21
|
+
'bool' => 'Qmf::TYPE_BOOL',
|
22
|
+
'deltaTime' => 'Qmf::TYPE_DELTATIME',
|
23
|
+
'double' => 'Qmf::TYPE_DOUBLE',
|
24
|
+
'float' => 'Qmf::TYPE_FLOAT',
|
25
|
+
'int16' => 'Qmf::TYPE_INT16',
|
26
|
+
'int32' => 'Qmf::TYPE_INT32',
|
27
|
+
'int64' => 'Qmf::TYPE_INT64',
|
28
|
+
'int' => 'Qmf::TYPE_INT64',
|
29
|
+
'int8' => 'Qmf::TYPE_INT8',
|
30
|
+
'list' => 'Qmf::TYPE_LIST', # XXX: is this right?
|
31
|
+
'lstr' => 'Qmf::TYPE_LSTR',
|
32
|
+
'string' => 'Qmf::TYPE_LSTR',
|
33
|
+
'map' => 'Qmf::TYPE_MAP',
|
34
|
+
'objId' => 'Qmf::TYPE_REF',
|
35
|
+
'sstr' => 'Qmf::TYPE_SSTR',
|
36
|
+
'uint16' => 'Qmf::TYPE_UINT16',
|
37
|
+
'uint32' => 'Qmf::TYPE_UINT32',
|
38
|
+
'uint64' => 'Qmf::TYPE_UINT64',
|
39
|
+
'uint' => 'Qmf::TYPE_UINT64',
|
40
|
+
'uint8' => 'Qmf::TYPE_UINT8',
|
41
|
+
'uuid' => 'Qmf::TYPE_UUID'
|
42
|
+
}
|
43
|
+
|
44
|
+
Access = {
|
45
|
+
"RC" => 'Qmf::ACCESS_READ_CREATE',
|
46
|
+
"RW" => 'Qmf::ACCESS_READ_WRITE',
|
47
|
+
"RO" => 'Qmf::ACCESS_READ_ONLY',
|
48
|
+
"R" => 'Qmf::ACCESS_READ_ONLY'
|
49
|
+
}
|
50
|
+
|
51
|
+
Direction = {
|
52
|
+
"I" => 'Qmf::DIR_IN',
|
53
|
+
"O" => 'Qmf::DIR_OUT',
|
54
|
+
"IO" => 'Qmf::DIR_IN_OUT',
|
55
|
+
"i" => 'Qmf::DIR_IN',
|
56
|
+
"o" => 'Qmf::DIR_OUT',
|
57
|
+
"io" => 'Qmf::DIR_IN_OUT',
|
58
|
+
"in" => 'Qmf::DIR_IN',
|
59
|
+
"out" => 'Qmf::DIR_OUT',
|
60
|
+
"inout" => 'Qmf::DIR_IN_OUT'
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# SPQR: Schema Processor for QMF/Ruby agents
|
2
|
+
#
|
3
|
+
# Manageable object mixin and support classes.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
6
|
+
#
|
7
|
+
# Author: William Benton (willb@redhat.com)
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
|
15
|
+
module SPQR
|
16
|
+
class ManageableMeta < Struct.new(:classname, :package, :description, :mmethods, :options, :statistics, :properties)
|
17
|
+
def initialize(*a)
|
18
|
+
super *a
|
19
|
+
self.options = (({} unless self.options) or self.options.dup)
|
20
|
+
self.statistics = [] unless self.statistics
|
21
|
+
self.properties = [] unless self.properties
|
22
|
+
self.mmethods ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
def declare_method(name, desc, options, blk=nil)
|
26
|
+
result = MethodMeta.new name, desc, options
|
27
|
+
blk.call(result.args) if blk
|
28
|
+
self.mmethods << result
|
29
|
+
self.mmethods[-1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def declare_statistic(name, kind, options)
|
33
|
+
declare_basic(:statistic, name, kind, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def declare_property(name, kind, options)
|
37
|
+
declare_basic(:property, name, kind, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def declare_basic(what, name, kind, options)
|
42
|
+
what_plural = "#{what.to_s.gsub(/y$/, 'ie')}s"
|
43
|
+
w_get = what_plural.to_sym
|
44
|
+
w_set = "#{what_plural}=".to_sym
|
45
|
+
|
46
|
+
self.send(w_set, (self.send(w_get) or []))
|
47
|
+
|
48
|
+
w_class = "#{what.to_s.capitalize}Meta"
|
49
|
+
self.send(w_get) << SPQR.const_get(w_class).new(name, kind, options)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class MethodMeta < Struct.new(:name, :description, :args, :options)
|
54
|
+
def initialize(*a)
|
55
|
+
super *a
|
56
|
+
self.options = (({} unless self.options) or self.options.dup)
|
57
|
+
self.args = gen_args
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def gen_args
|
62
|
+
result = []
|
63
|
+
|
64
|
+
def result.declare(name, kind, direction, description=nil, options=nil)
|
65
|
+
options ||= {}
|
66
|
+
arg = ::SPQR::ArgMeta.new name, kind, direction, description, options.dup
|
67
|
+
self << arg
|
68
|
+
end
|
69
|
+
|
70
|
+
result
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ArgMeta < Struct.new(:name, :kind, :direction, :description, :options)
|
75
|
+
def initialize(*a)
|
76
|
+
super *a
|
77
|
+
self.options = (({} unless self.options) or self.options.dup)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class PropertyMeta < Struct.new(:name, :kind, :options)
|
82
|
+
def initialize(*a)
|
83
|
+
super *a
|
84
|
+
self.options = (({} unless self.options) or self.options.dup)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class StatisticMeta < Struct.new(:name, :kind, :options)
|
89
|
+
def initialize(*a)
|
90
|
+
super *a
|
91
|
+
self.options = (({} unless self.options) or self.options.dup)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
module ManageableClassMixins
|
96
|
+
def spqr_meta
|
97
|
+
@spqr_meta ||= ::SPQR::ManageableMeta.new
|
98
|
+
end
|
99
|
+
|
100
|
+
def spqr_logger=(logger)
|
101
|
+
@spqr_log = logger
|
102
|
+
end
|
103
|
+
|
104
|
+
def spqr_logger
|
105
|
+
@spqr_log || ::SPQR::Sink.new
|
106
|
+
end
|
107
|
+
|
108
|
+
# Exposes a method to QMF
|
109
|
+
def spqr_expose(name, description=nil, options=nil, &blk)
|
110
|
+
spqr_meta.declare_method(name, description, options, blk)
|
111
|
+
end
|
112
|
+
|
113
|
+
def spqr_package(nm)
|
114
|
+
spqr_meta.package = nm
|
115
|
+
end
|
116
|
+
|
117
|
+
def spqr_class(nm)
|
118
|
+
spqr_meta.classname = nm
|
119
|
+
end
|
120
|
+
|
121
|
+
def spqr_description(d)
|
122
|
+
spqr_meta.description = d
|
123
|
+
end
|
124
|
+
|
125
|
+
def spqr_options(opts)
|
126
|
+
spqr_meta.options = opts.dup
|
127
|
+
end
|
128
|
+
|
129
|
+
def spqr_statistic(name, kind, options=nil)
|
130
|
+
spqr_meta.declare_statistic(name, kind, options)
|
131
|
+
|
132
|
+
self.class_eval do
|
133
|
+
# XXX: are we only interested in declaring a reader for
|
134
|
+
# statistics? Doesn't it really makes more sense for the managed
|
135
|
+
# class to declare a method with the same name as the
|
136
|
+
# statistic so we aren't declaring anything at all here?
|
137
|
+
|
138
|
+
# XXX: should cons up a "safe_attr_reader" method that works
|
139
|
+
# like this:
|
140
|
+
attr_reader name.to_sym unless instance_methods.include? "#{name}"
|
141
|
+
attr_writer name.to_sym unless instance_methods.include? "#{name}="
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def spqr_property(name, kind, options=nil)
|
146
|
+
spqr_meta.declare_property(name, kind, options)
|
147
|
+
|
148
|
+
# add a property accessor to instances of other
|
149
|
+
self.class_eval do
|
150
|
+
# XXX: should cons up a "safe_attr_accessor" method that works like this:
|
151
|
+
attr_reader name.to_sym unless instance_methods.include? "#{name}"
|
152
|
+
attr_writer name.to_sym unless instance_methods.include? "#{name}="
|
153
|
+
end
|
154
|
+
|
155
|
+
if options and options[:index]
|
156
|
+
# if this is an index property, add a find-by method if one
|
157
|
+
# does not already exist
|
158
|
+
spqr_define_index_find(name)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
def spqr_define_index_find(name)
|
164
|
+
find_by_prop = "find_by_#{name}".to_sym
|
165
|
+
|
166
|
+
return if self.respond_to? find_by_prop
|
167
|
+
|
168
|
+
define_method find_by_prop do |arg|
|
169
|
+
raise "#{self} must define find_by_#{name}(arg)"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
module Manageable
|
175
|
+
def qmf_oid
|
176
|
+
result = 0
|
177
|
+
if self.respond_to? :spqr_object_id
|
178
|
+
result = spqr_object_id
|
179
|
+
else
|
180
|
+
result = object_id
|
181
|
+
end
|
182
|
+
|
183
|
+
result & 0x7fffffff
|
184
|
+
end
|
185
|
+
|
186
|
+
def qmf_id
|
187
|
+
[qmf_oid, self.class.class_id]
|
188
|
+
end
|
189
|
+
|
190
|
+
def log
|
191
|
+
self.class.spqr_logger
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.included(other)
|
195
|
+
class << other
|
196
|
+
include ManageableClassMixins
|
197
|
+
end
|
198
|
+
|
199
|
+
unless other.respond_to? :find_by_id
|
200
|
+
def other.find_by_id(id)
|
201
|
+
raise "#{self} must define find_by_id(id)"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
unless other.respond_to? :find_all
|
206
|
+
def other.find_all
|
207
|
+
raise "#{self} must define find_all"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
unless other.respond_to? :class_id
|
212
|
+
def other.class_id
|
213
|
+
package_list = spqr_meta.package.to_s.split(".")
|
214
|
+
cls = spqr_meta.classname.to_s or self.name.to_s
|
215
|
+
((package_list.map {|pkg| pkg.capitalize} << cls).join("::")).hash & 0x7fffffff
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
other.spqr_class other.name.to_sym
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/spqr/spqr.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# SPQR: Schema Processor for QMF/Ruby agents
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton (willb@redhat.com)
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
require 'spqr/utils'
|
14
|
+
require 'spqr/constants'
|
15
|
+
require 'spqr/codegen'
|
16
|
+
require 'spqr/manageable'
|
data/lib/spqr/utils.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Utility functions and modules for SPQR
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009 Red Hat, Inc.
|
4
|
+
#
|
5
|
+
# Author: William Benton (willb@redhat.com)
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
module SPQR
|
14
|
+
class Sink
|
15
|
+
def method_missing(*args)
|
16
|
+
yield if block_given?
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module PrettyPrinter
|
22
|
+
def writemode
|
23
|
+
$PP_WRITEMODE ||= File::WRONLY|File::CREAT|File::TRUNC
|
24
|
+
end
|
25
|
+
|
26
|
+
def stack
|
27
|
+
@fstack ||= [STDOUT]
|
28
|
+
end
|
29
|
+
|
30
|
+
def inc_indent
|
31
|
+
@indent = indent + 2
|
32
|
+
end
|
33
|
+
|
34
|
+
def dec_indent
|
35
|
+
@indent = indent - 2
|
36
|
+
end
|
37
|
+
|
38
|
+
def indent
|
39
|
+
@indent ||= 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def outfile
|
43
|
+
@fstack[-1] or STDOUT
|
44
|
+
end
|
45
|
+
|
46
|
+
def pp(s)
|
47
|
+
outfile.puts "#{' ' * indent}#{s}\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
def pp_decl(kind, name, etc=nil)
|
51
|
+
pp "#{kind} #{name}#{etc}"
|
52
|
+
inc_indent
|
53
|
+
yield if block_given?
|
54
|
+
dec_indent
|
55
|
+
pp "end"
|
56
|
+
end
|
57
|
+
|
58
|
+
def pp_call(callable, args)
|
59
|
+
arg_repr = args.map {|arg| (arg.inspect if arg.kind_of? Hash) or arg}.join(', ')
|
60
|
+
pp "#{callable}(#{arg_repr})"
|
61
|
+
end
|
62
|
+
|
63
|
+
def pp_invoke(receiver, method, args)
|
64
|
+
pp_call "#{receiver}.#{method}", args
|
65
|
+
end
|
66
|
+
|
67
|
+
def with_output_to(filename, &action)
|
68
|
+
File::open(filename, writemode) do |of|
|
69
|
+
stack << of
|
70
|
+
action.call
|
71
|
+
stack.pop
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module MiscUtil
|
77
|
+
def symbolize_dict(k, kz=nil)
|
78
|
+
k2 = {}
|
79
|
+
kz ||= k.keys
|
80
|
+
|
81
|
+
k.keys.each do |key|
|
82
|
+
k2[key.to_sym] = k[key] if (kz.include?(key) or kz.include?(key.to_sym))
|
83
|
+
end
|
84
|
+
|
85
|
+
k2
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
data/spec/spqr_spec.rb
ADDED
data/spqr.spec.in
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Generated from <%= File::basename(format.gem_path) %> by gem2rpm -*- rpm-spec -*-
|
2
|
+
%define ruby_sitelib %(ruby -rrbconfig -e "puts Config::CONFIG['sitelibdir']")
|
3
|
+
%define gemdir %(ruby -rubygems -e 'puts Gem::dir' 2>/dev/null)
|
4
|
+
%define gemname <%= spec.name %>
|
5
|
+
%define geminstdir %{gemdir}/gems/%{gemname}-%{version}
|
6
|
+
|
7
|
+
Summary: <%= spec.summary.gsub(/\.$/, "") %>
|
8
|
+
Name: rubygem-%{gemname}
|
9
|
+
Version: <%= spec.version %>
|
10
|
+
Release: 1%{?dist}
|
11
|
+
Group: Development/Languages
|
12
|
+
License: ASL 2.0
|
13
|
+
URL: <%= spec.homepage %>
|
14
|
+
Source0: <%= download_path %>%{gemname}-%{version}.gem
|
15
|
+
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
16
|
+
Requires: rubygems
|
17
|
+
Requires: ruby(abi) = 1.8
|
18
|
+
Requires: ruby-qmf
|
19
|
+
<% for d in spec.dependencies %>
|
20
|
+
<% for req in d.version_requirements.to_rpm %>
|
21
|
+
Requires: rubygem(<%= d.name %>) <%= req %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
BuildRequires: ruby > 1.8
|
25
|
+
BuildRequires: rubygems
|
26
|
+
<% if spec.extensions.empty? %>
|
27
|
+
BuildArch: noarch
|
28
|
+
<% end %>
|
29
|
+
Provides: rubygem(%{gemname}) = %{version}
|
30
|
+
|
31
|
+
%description
|
32
|
+
<%= spec.description.to_s.chomp.word_wrap(78) + "\n" %>
|
33
|
+
|
34
|
+
<% if nongem %>
|
35
|
+
%package -n ruby-%{gemname}
|
36
|
+
Summary: <%= spec.summary.gsub(/\.$/, "") %>
|
37
|
+
Group: Development/Languages
|
38
|
+
Requires: rubygem(%{gemname}) = %{version}
|
39
|
+
<% spec.files.select{ |f| spec.require_paths.include?(File::dirname(f)) }.reject { |f| f =~ /\.rb$/ }.collect { |f| File::basename(f) }.each do |p| %>
|
40
|
+
Provides: ruby(<%= p %>) = %{version}
|
41
|
+
<% end %>
|
42
|
+
%description -n ruby-%{gemname}
|
43
|
+
<%= spec.description.to_s.chomp.word_wrap(78) + "\n" %>
|
44
|
+
<% end # if nongem %>
|
45
|
+
|
46
|
+
%prep
|
47
|
+
|
48
|
+
%build
|
49
|
+
|
50
|
+
%install
|
51
|
+
rm -rf %{buildroot}
|
52
|
+
mkdir -p %{buildroot}%{gemdir}
|
53
|
+
<% rdoc_opt = spec.has_rdoc ? "--rdoc " : "" %>
|
54
|
+
gem install --local --install-dir %{buildroot}%{gemdir} \
|
55
|
+
--force <%= rdoc_opt %>%{SOURCE0}
|
56
|
+
<% unless spec.executables.empty? %>
|
57
|
+
mkdir -p %{buildroot}/%{_bindir}
|
58
|
+
mv %{buildroot}%{gemdir}/bin/* %{buildroot}/%{_bindir}
|
59
|
+
rmdir %{buildroot}%{gemdir}/bin
|
60
|
+
find %{buildroot}%{geminstdir}/bin -type f | xargs chmod a+x
|
61
|
+
<% end %>
|
62
|
+
<% if nongem %>
|
63
|
+
mkdir -p %{buildroot}%{ruby_sitelib}
|
64
|
+
<% spec.files.select{ |f| spec.require_paths.include?(File::dirname(f)) }.each do |p| %>
|
65
|
+
ln -s %{gemdir}/gems/%{gemname}-%{version}/<%= p %> %{buildroot}%{ruby_sitelib}
|
66
|
+
<% end %>
|
67
|
+
<% end # if nongem %>
|
68
|
+
|
69
|
+
%clean
|
70
|
+
rm -rf %{buildroot}
|
71
|
+
|
72
|
+
%files
|
73
|
+
%defattr(-, root, root, -)
|
74
|
+
<% for f in spec.executables %>
|
75
|
+
%{_bindir}/<%= f %>
|
76
|
+
<% end %>
|
77
|
+
%{gemdir}/gems/%{gemname}-%{version}/
|
78
|
+
<% if spec.has_rdoc %>
|
79
|
+
%doc %{gemdir}/doc/%{gemname}-%{version}
|
80
|
+
<% end %>
|
81
|
+
<% for f in spec.extra_rdoc_files %>
|
82
|
+
%doc %{geminstdir}/<%= f %>
|
83
|
+
<% end %>
|
84
|
+
%{gemdir}/cache/%{gemname}-%{version}.gem
|
85
|
+
%{gemdir}/specifications/%{gemname}-%{version}.gemspec
|
86
|
+
|
87
|
+
<% if nongem %>
|
88
|
+
%files -n ruby-%{gemname}
|
89
|
+
%defattr(-, root, root, -)
|
90
|
+
%{ruby_sitelib}/*
|
91
|
+
<% end # if nongem %>
|
92
|
+
|
93
|
+
%changelog
|
94
|
+
* <%= Time.now.strftime("%a %b %d %Y") %> <%= packager %> - <%= spec.version %>-1
|
95
|
+
- Initial package
|