inventory 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0babb6ccf5f1ffb7268ba69e57a9ade4e17c758e
4
+ data.tar.gz: 0b55936c52037e6a59796e9c85a15d0802fcbe32
5
+ SHA512:
6
+ metadata.gz: 78108a236b43f9b9d012c3d3be3c7408b502d872f3390f6b4bce552a880563bca58d2d27c70fd8339555b011c437d8c8ad374aa2b43acacbf9dc42d3627f6380
7
+ data.tar.gz: cfe46f91c44dd8c3d22802ceb318f2ad0c93648235bb7ea48d0f37effb9ec3b3dbb9e5dc390cd5ff66ed4d93fad7d023d6b7e906d8eab400ff13addaba65db7e
data/README CHANGED
@@ -1,6 +1,213 @@
1
1
  Inventory
2
2
 
3
- Inventory allows you to create inventories of your Ruby projects. These
4
- inventories can then be used to load the project, create gem specifications
5
- and gems, run unit tests, and verify that the project’s content is what you
6
- think it is.
3
+ Inventory keeps track of the contents of your Ruby¹ projects. Such an
4
+ inventory can be used to load the project, create gem specifications and
5
+ gems, run unit tests, compile extensions, and verify that the project’s
6
+ content is what you think it is.
7
+
8
+ ¹ See http://ruby-lang.org/
9
+
10
+ § Usage
11
+
12
+ Let’s begin by discussing the project structure that Inventory expects you
13
+ to use. It’s pretty much exactly the same as the standard Ruby project
14
+ structure¹:
15
+
16
+ ├── README
17
+ ├── Rakefile
18
+ ├── lib
19
+ │ ├── foo-1.0
20
+ │ │ ├── bar.rb
21
+ │ │ └── version.rb
22
+ │ └── foo-1.0.rb
23
+ └── test
24
+ └── unit
25
+ ├── foo-1.0
26
+ │ ├── bar.rb
27
+ │ └── version.rb
28
+ └── foo-1.0.rb
29
+
30
+ Here you see a simplified version of a project called “Foo”’s project
31
+ structure. The only real difference from the standard is that the main
32
+ entry point into the library is named “foo-1.0.rb” instead of “foo.rb” and
33
+ that the root sub-directory of “lib” is similarly named “foo-1.0” instead
34
+ of “foo”. The difference is the inclusion of the API version. This must
35
+ be the major version of the project followed by a constant “.0”. The
36
+ reason for this is that it allows concurrent installations of different
37
+ major versions of the project and means that the wrong version will never
38
+ accidentally be loaded with require.
39
+
40
+ There’s a bigger difference in the content of the files.
41
+ ‹Lib/foo-1.0/version.rb› will contain our inventory instead of a String:
42
+
43
+ require 'inventory-1.0'
44
+
45
+ class Foo
46
+ Version = Foo.new(1, 4, 0){
47
+ def dependencies
48
+ super + Dependencies.new{
49
+ development 'baz', 1, 3, 0
50
+ runtime 'goo', 2, 0, 0
51
+ optional 'roo-loo', 3, 0, 0, :feature => 'roo-loo'
52
+ }
53
+ end
54
+
55
+ def package_libs
56
+ %w[bar.rb]
57
+ end
58
+ }
59
+ end
60
+
61
+ We’re introducing quite a few concepts at once, and we’ll look into each in
62
+ greater detail, but we begin by setting the ‹Version› constant to a new
63
+ instance of an Inventory with major, minor, and patch version atoms 1, 4,
64
+ and 0. Then we add a couple of dependencies and list the library files
65
+ that are included in this project.
66
+
67
+ The version numbers shouldn’t come as a surprise. These track the version
68
+ of the API that we’re shipping using {semantic versioning}². They also
69
+ allow the Inventory#to_s method to act as if you’d defined Version as
70
+ ‹'1.4.0'›.
71
+
72
+ We then extend the definition of ‹dependencies› by adding another set of
73
+ dependencies to ‹super›. ‹Super› includes a dependency on the version of
74
+ the inventory project that’s being used with this project, so you’ll never
75
+ have to list that yourself. The other three dependencies are all of
76
+ different kinds: development, runtime, and optional. A development
77
+ dependency is one that’s required while developing the project, for
78
+ example, a unit-testing framework, a documentation generator, and so on.
79
+ Runtime dependencies are requirements of the project to be able to run,
80
+ both during development and when installed. Finally, optional dependencies
81
+ are runtime dependencies that may or may not be required during execution.
82
+ The difference between runtime and optional is that the inventory won’t try
83
+ to automatically load an optional dependency, instead leaving that up to
84
+ you to do when and if it becomes necessary. By that logic, runtime
85
+ dependencies will be automatically loaded, which is a good reason for
86
+ having dependency information available at runtime.
87
+
88
+ The version numbers of dependencies also use semantic versioning, but note
89
+ that the patch atom is ignored unless the major atom is 0. You should
90
+ always only depend on the major and minor atoms.
91
+
92
+ As mentioned, runtime dependencies will be automatically loaded and the
93
+ feature they try to load is based on the name of the dependency with a
94
+ “-X.0” tacked on the end, where ‘X’ is the major version of the dependency.
95
+ Sometimes, this isn’t correct, in which case the :feature option may be
96
+ given to specify the name of the feature.
97
+
98
+ You may also override other parts of a dependency by passing in a block to
99
+ the dependency, much like we’re doing for inventories.
100
+
101
+ The rest of an inventory will list the various files included in the
102
+ project. This project only consists of one additional file to those that
103
+ an inventory automatically include (Rakefile, README, the main entry point,
104
+ and the version.rb file that defines the inventory itself), namely the
105
+ library file ‹bar.rb›. Library files will be loaded automatically when the
106
+ main entry point file loads the inventory. Library files that shouldn’t be
107
+ loaded may be listed under a different heading, namely “additional_libs”.
108
+ Both these sets of files will be used to generate a list of unit test files
109
+ automatically, so each library file will have a corresponding unit test
110
+ file in the inventory. We’ll discuss the different headings of an
111
+ inventory in more detail later on.
112
+
113
+ Now that we’ve written our inventory, let’s set it up so that it’s content
114
+ gets loaded when our main entry point gets loaded. We add the following
115
+ piece of code to ‹lib/foo-1.0.rb›:
116
+
117
+ module Foo
118
+ load File.expand_path('../foo-1.0/version.rb', __FILE__)
119
+ Version.load
120
+ end
121
+
122
+ That’s all there’s to it.
123
+
124
+ The inventory can also be used to great effect from a Rakefile using a
125
+ separate project called Inventory-Rake³. Using it’ll give us tasks for
126
+ cleaning up our project, compiling extensions, installing dependencies,
127
+ installing and uninstalling the project itself, and creating and pushing
128
+ distribution files to distribution points.
129
+
130
+ require 'inventory-rake-1.0'
131
+
132
+ load File.expand_path('../lib/foo-1.0/version.rb', __FILE__)
133
+
134
+ Inventory::Rake::Tasks.define Foo::Version, :gem => proc{ |_, s|
135
+ s.author = 'Your Name'
136
+ s.email = 'you@example.com'
137
+ s.homepage = 'https://example.com/'
138
+ }
139
+
140
+ Inventory::Rake::Tasks.unless_installing_dependencies do
141
+ require 'lookout-rake-3.0'
142
+ Lookout::Rake::Tasks::Test.new
143
+ end
144
+
145
+ It’s ‹Inventory::Rake::Tasks.define› that does the heavy lifting. It takes
146
+ our inventory and sets up the tasks mentioned above. We also do some
147
+ additional customization of the gem specification.
148
+
149
+ As we want to be able to use our Rakefile to install our dependencies for
150
+ us, the rest of the Rakefile is inside the conditional
151
+ #unless_installing_dependencies, which, as the name certainly implies,
152
+ executes its block unless the task being run is the one that installs our
153
+ dependencies. This becomes relevant when we set up Travis⁴ integration
154
+ next. The only conditional set-up we do in our Rakefile is creating our
155
+ test task via Lookout-Rake⁵, which also uses our inventory to find the unit
156
+ tests to run when executed.
157
+
158
+ Travis integration is straightforward. Simply put
159
+
160
+ before_script:
161
+ - gem install inventory-rake -v '~> VERSION' --no-rdoc --no-ri
162
+ - rake gem:deps:install
163
+
164
+ in the project’s ‹.travis.yml› file, replacing ‹VERSION› with the version
165
+ of Inventory-Rake that you require. This’ll make sure that Travis installs
166
+ all development, runtime, and optional dependencies that you’ve listed in
167
+ your inventory before running any tests.
168
+
169
+ You might also need to put
170
+
171
+ env:
172
+ - RUBYOPT=rubygems
173
+
174
+ in your ‹.travis.yml› file, depending on how things are set up.
175
+
176
+ ¹ Ruby project structure: http://guides.rubygems.org/make-your-own-gem/
177
+ ² Semantic versioning: http://semver.org/
178
+ ³ Inventory-Rake: http://disu.se/software/inventory-rake/
179
+ ⁴ Travis: http://travis-ci.org/
180
+ ⁵ Lookout-Rake: http://disu.se/software/lookout-rake/
181
+
182
+ § API
183
+
184
+ If the guide above doesn’t provide you with all the answers you seek, you
185
+ may refer to the API¹ for more answers.
186
+
187
+ ¹ See http://disu.se/software/inventory/api/inventory/
188
+
189
+ § Financing
190
+
191
+ Currently, most of my time is spent at my day job and in my rather busy
192
+ private life. Please motivate me to spend time on this piece of software
193
+ by donating some of your money to this project. Yeah, I realize that
194
+ requesting money to develop software is a bit, well, capitalistic of me.
195
+ But please realize that I live in a capitalistic society and I need money
196
+ to have other people give me the things that I need to continue living
197
+ under the rules of said society. So, if you feel that this piece of
198
+ software has helped you out enough to warrant a reward, please PayPal a
199
+ donation to now@disu.se¹. Thanks! Your support won’t go unnoticed!
200
+
201
+ ¹ Send a donation:
202
+ https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=now%40disu%2ese&item_name=Nikolai%20Weibull%20Software%20Services
203
+
204
+ § Reporting Bugs
205
+
206
+ Please report any bugs that you encounter to the {issue tracker}¹.
207
+
208
+ ¹ See https://github.com/now/inventory/issues
209
+
210
+ § Authors
211
+
212
+ Nikolai Weibull wrote the code, the tests, the documentation, and this
213
+ README.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@
3
3
  $:.unshift File.expand_path('../lib', __FILE__)
4
4
  require 'inventory-1.0'
5
5
 
6
- require 'inventory/rake-1.0'
6
+ require 'inventory-rake-1.0'
7
7
 
8
8
  Inventory::Rake::Tasks.define Inventory::Version, :gem => proc{ |_, s|
9
9
  s.author = 'Nikolai Weibull'
@@ -12,6 +12,12 @@ Inventory::Rake::Tasks.define Inventory::Version, :gem => proc{ |_, s|
12
12
  }
13
13
 
14
14
  Inventory::Rake::Tasks.unless_installing_dependencies do
15
- require 'lookout/rake-3.0'
15
+ require 'lookout-rake-3.0'
16
16
  Lookout::Rake::Tasks::Test.new
17
+
18
+ require 'inventory-rake-tasks-yard-1.0'
19
+ Inventory::Rake::Tasks::YARD.new do |t|
20
+ t.options += %w'--plugin yard-heuristics-1.0'
21
+ t.globals[:source_code_url] = 'https://github.com/now/%s/blob/v%s/%%s#L%%d' % [t.inventory.package, t.inventory]
22
+ end
17
23
  end
@@ -1,32 +1,94 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ # An Inventory keeps track of your Ruby project’s {Inventory#to_s version} and
4
+ # its {Inventory#to_a content}. It also allows you to {#load} your project’s
5
+ # source files in a simple manner and track its {#dependencies}. Add-ons, such
6
+ # as [Inventory-Rake](http://disu.se/software/inventory-rake/) and
7
+ # [Inventory-Rake-Tasks-YARD](http://disu.se/software/inventory-rake-tasks-yard),
8
+ # can also use this information to great effect.
9
+ #
10
+ # The basic usage pattern is to set your project’s Version constant to an
11
+ # instance of this class, where you set the version information and override
12
+ # the relevant methods with the information relevant to your project. For
13
+ # example, almost all project will want to override the {#package_libs} method
14
+ # with a method that returns an Array of the files relevant to it.
15
+ #
16
+ # Quite a few methods have definitions that you’ll want to extend, for example,
17
+ # {#dependencies}, in which case you simply call super and add your additions
18
+ # to its result.
19
+ #
20
+ # The naming convention of methods is that any method named “X_files” will
21
+ # return an Array of complete paths to files of kind X inside the project,
22
+ # while any method named “X” will return an Array of paths relative to the
23
+ # sub-directory that the method is named after. For example, “lib_files”
24
+ # returns an Array of complete paths to files under the “lib” directory
25
+ # ([lib/foo-1.0.rb, …]), while “libs” returns an Array of paths relative to the
26
+ # “lib” directory ([foo-1.0.rb]).
27
+ #
28
+ # See {Dependencies} for more information on managing dependencies.
29
+ #
30
+ # See {Extension} for more information on adding extensions.
31
+ #
32
+ # @example Creating an Inventory
33
+ # require 'inventory-1.0'
34
+ #
35
+ # class Foo
36
+ # Version = Inventory.new(1, 2, 0){
37
+ # def dependencies
38
+ # super + Dependencies.new{
39
+ # development 'inventory-rake', 1, 3, 0
40
+ # runtime 'bar', 1, 6, 0
41
+ # }
42
+ # end
43
+ #
44
+ # def package_libs
45
+ # %w'a.rb
46
+ # b.rb
47
+ # b/c.rb'
48
+ # end
49
+ # }
50
+ # end
3
51
  class Inventory
52
+ # Sets up an inventory for version MAJOR.MINOR.PATCH for a library whose
53
+ # `lib/PACKAGE/version.rb` is at PATH. Any block will be #instance_exec’d.
54
+ #
55
+ # @param major [String]
56
+ # @param minor [String]
57
+ # @param patch [String]
58
+ # @param path [String]
59
+ # @raise [ArgumentError] If PATH’s default value can’t be calculated
60
+ # @raise [ArgumentError] If PATH isn’t of the form `lib/PACKAGE/version.rb`
4
61
  def initialize(major, minor, patch,
5
- path = (m = /\A(.*):\d+(?::in .*)?\z/.match(caller.first) and m[1]),
6
- &block)
62
+ path = (m = /\A(.*):\d+(?::in .*)?\z/.match(caller.first) and m[1]))
7
63
  @major, @minor, @patch = major, minor, patch
8
64
  raise ArgumentError, 'default value of path argument could not be calculated' unless path
9
65
  @path = path
10
66
  @srcdir, _, @package_path = File.dirname(File.expand_path(path)).rpartition('/lib/')
11
- @package_require = '%s-%d.0' % [package_path, major]
67
+ @package = @package_path.sub(/-#{Regexp.escape(major.to_s)}\.0\z/, '').gsub('/', '-')
68
+ @package_require = package == package_path.gsub('/', '-') ? '%s-%d.0' % [package_path, major] : package_path
12
69
  raise ArgumentError,
13
70
  'path is not of the form PATH/lib/PACKAGE/version.rb: %s' % path if
14
71
  @srcdir.empty?
15
72
  instance_exec(&Proc.new) if block_given?
16
73
  end
17
74
 
18
- def package
19
- package_path.gsub('/', '-')
20
- end
75
+ # @return [String] The name of the package, as derived from the
76
+ # {#package_path}
77
+ attr_reader :package
21
78
 
22
79
  def version_require
80
+ warn '%s#%s is deprecated and there’s no replacement' % [self.class, __method__]
23
81
  File.join(package_path, 'version')
24
82
  end
25
83
 
26
84
  def lib_directories
27
- %w'lib'
85
+ %w[lib]
28
86
  end
29
87
 
88
+ # Requires all {#requires}, {Dependencies#require requires} all
89
+ # {#dependencies}, and loads all {#loads}.
90
+ #
91
+ # @return [self]
30
92
  def load
31
93
  requires.each do |requirement|
32
94
  require requirement
@@ -38,56 +100,96 @@ class Inventory
38
100
  self
39
101
  end
40
102
 
103
+ # @return [Dependencies] The dependencies of the package
104
+ # @note The default list of dependencies is an {Dependencies#optional
105
+ # optional} dependency on the inventory package itself.
41
106
  def dependencies
42
107
  Dependencies.new{
43
108
  optional 'inventory', Version.major, Version.minor, Version.patch
44
109
  }
45
110
  end
46
111
 
112
+ # @return [Array<String>] The files to require when {#load}ing, the default
113
+ # being empty
47
114
  def requires
48
115
  []
49
116
  end
50
117
 
118
+ # @return [Array<String>] The files to load when {#load}ing, the default
119
+ # being {#libs}
51
120
  def loads
52
121
  libs
53
122
  end
54
123
 
55
- def libs
124
+ # @return [Array<Extension>] The extensions included in the package, the
125
+ # default being empty
126
+ def extensions
56
127
  []
57
128
  end
58
129
 
130
+ # @return [Array<String>] The library files belonging to the package that will
131
+ # be loaded when {#load}ing, the default being empty
132
+ def package_libs
133
+ []
134
+ end
135
+
136
+ # @return [Array<String>] The library files to load when {#load}ing, the
137
+ # default being {#package_libs} inside a directory with the same name as
138
+ # {#package_require}
139
+ def libs
140
+ package_libs.map{ |e| '%s/%s' % [package_path, e] }
141
+ end
142
+
143
+ # @return [Array<String>] Any additional library files, the default being
144
+ # `{#package_path}-{#major}.0.rb` and `{#package_path}/version.rb`
59
145
  def additional_libs
60
- [package_require, version_require].map{ |e| '%s.rb' % e }
146
+ [package_require, File.join(package_path, 'version')].map{ |e| '%s.rb' % e }
61
147
  end
62
148
 
149
+ # @return [Array<String>] All library files, that is, {#libs} +
150
+ # {#additional_libs}
63
151
  def all_libs
64
152
  libs + additional_libs
65
153
  end
66
154
 
155
+ # @return [Array<String>] The complete paths of {#all_libs all library files}
156
+ # inside the package
67
157
  def lib_files
68
158
  all_libs.map{ |e| 'lib/%s' % e }
69
159
  end
70
160
 
161
+ # @return [Array<String>] The unit tests included in the package, the default
162
+ # being {#all_libs}, meaning that there’s one unit test for each library
163
+ # file
71
164
  def unit_tests
72
165
  all_libs
73
166
  end
74
167
 
168
+ # @return [Array<String>] Any additional unit tests, the default being empty
75
169
  def additional_unit_tests
76
170
  []
77
171
  end
78
172
 
173
+ # @return [Array<String>] All unit tests, that is {#unit_tests} +
174
+ # {#additional_unit_tests}
79
175
  def all_unit_tests
80
176
  unit_tests + additional_unit_tests
81
177
  end
82
178
 
179
+ # @return [Array<String>] The complete paths of {#all_unit_tests all unit
180
+ # test} files inside the package
83
181
  def unit_test_files
84
182
  all_unit_tests.map{ |e| 'test/unit/%s' % e }
85
183
  end
86
184
 
185
+ # @return [Array<String>] All test files included in the package, the default
186
+ # being {#unit_test_files}
87
187
  def test_files
88
188
  unit_test_files
89
189
  end
90
190
 
191
+ # @return [Array<String>] Any additional files included in the package, the
192
+ # default being README and Rakefile
91
193
  def additional_files
92
194
  %w'
93
195
  README
@@ -95,14 +197,19 @@ class Inventory
95
197
  '
96
198
  end
97
199
 
200
+ # @return [Array<String>] All files included in the package, that is
201
+ # {#lib_files} + {#test_files} + {#additional_files} + all files from
202
+ # {#extensions}
98
203
  def files
99
- lib_files + test_files + additional_files
204
+ lib_files + test_files + additional_files + extensions.map(&:files).flatten
100
205
  end
101
206
 
207
+ # @return [Array<String>] Whatever {#files} returns
102
208
  def to_a
103
209
  files
104
210
  end
105
211
 
212
+ # @return [String] The version atoms formatted as {#major}.{#minor}.{#patch}
106
213
  def to_s
107
214
  '%d.%d.%d' % [major, minor, patch]
108
215
  end
@@ -111,9 +218,29 @@ class Inventory
111
218
  '#<%s: %s %s>' % [self.class, package, self]
112
219
  end
113
220
 
114
- attr_reader :major, :minor, :patch, :path, :srcdir, :package_path, :package_require
221
+ # @return [Integer] The major version atom of the package
222
+ attr_reader :major
223
+
224
+ # @return [Integer] The minor version atom of the package
225
+ attr_reader :minor
226
+
227
+ # @return [Integer] The patch version atom of the package
228
+ attr_reader :patch
229
+
230
+ # @return [String] The path to the file containing the inventory
231
+ attr_reader :path
232
+
233
+ # @return [String] The top-level path of the package
234
+ attr_reader :srcdir
235
+
236
+ # @return [String] The root sub-directory under the “lib” directory of the
237
+ # package of the package
238
+ attr_reader :package_path
239
+
240
+ # @return [String] The feature to require for the package
241
+ attr_reader :package_require
115
242
 
116
- load File.expand_path('../inventory/version.rb', __FILE__)
243
+ load File.expand_path('../inventory-1.0/version.rb', __FILE__)
117
244
  Version.loads.each do |load|
118
245
  Kernel.load File.expand_path('../%s' % load, __FILE__)
119
246
  end