inventory 1.3.0 → 1.4.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.
@@ -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