needle-extras 1.0.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,202 @@
1
+ # --------------------------------------------------------------------------
2
+ # Portions of this code were taken from the "poignant.rb" script created
3
+ # by whytheluckystiff, which generates "Why's (Poignant) Guide to Ruby".
4
+ # The original script may be obtained from the poignant CVS repository,
5
+ # at http://poignant.rubyforge.org.
6
+ #
7
+ # This script is distributed under the by-sa/1.0 Creative Commons license.
8
+ # --------------------------------------------------------------------------
9
+
10
+ require 'erb'
11
+ require 'fileutils'
12
+ require 'yaml'
13
+ require 'redcloth'
14
+
15
+ module Needle
16
+ module Manual
17
+
18
+ class Manual
19
+ attr_accessor :product, :meta, :chapters, :examples, :recent_updates
20
+
21
+ def Manual.load( file_name )
22
+ File.open( file_name ) { |file| YAML.load( file ) }
23
+ end
24
+ end
25
+
26
+ class Meta
27
+ attr_accessor :copyright, :author, :email
28
+ end
29
+
30
+ class Product
31
+ attr_accessor :name, :tagline, :version, :logo, :urls, :project
32
+ end
33
+
34
+ class Sidebar
35
+ attr_accessor :title, :content
36
+ end
37
+
38
+ class Chapter
39
+ attr_accessor :index, :title, :sections
40
+
41
+ def initialize( index, title, sections )
42
+ @index = index
43
+ @title = title
44
+
45
+ section_index = 0
46
+ @sections = ( sections || [] ).collect do |section|
47
+ section_index += 1
48
+ if section.respond_to? :keys
49
+ Section.new( section_index, section.keys.first, section.values.first )
50
+ else
51
+ section_index -= 1
52
+ Section.new( section_index, nil, section )
53
+ end
54
+ end
55
+ end
56
+
57
+ def page_title
58
+ "Chapter #{index}: #{title}"
59
+ end
60
+ end
61
+
62
+ class Tutorial
63
+ attr_accessor :index, :title, :brief, :intro, :steps, :summary
64
+
65
+ def initialize( index, title, brief, intro, steps, summary )
66
+ @index = index
67
+ @title = title
68
+ @brief = RedCloth.new( brief )
69
+ @intro = RedCloth.new( intro ) if intro
70
+ @summary = RedCloth.new( summary ) if summary
71
+ @steps = steps.map { |step| RedCloth.new( step ) }
72
+ end
73
+
74
+ def page_title
75
+ "Tutorial #{index}: #{title}"
76
+ end
77
+ end
78
+
79
+ class Example
80
+ attr_accessor :index, :title, :brief, :intro, :design, :implementation, :summary
81
+
82
+ def initialize( index, title, brief, intro, design, implementation, summary )
83
+ @index = index
84
+ @title = title
85
+ @brief = RedCloth.new( brief )
86
+ @intro = RedCloth.new( intro )
87
+ @design = RedCloth.new( design )
88
+ @implementation = RedCloth.new( implementation )
89
+ @summary = RedCloth.new( summary )
90
+ end
91
+
92
+ def page_title
93
+ "Example #{index}: #{title}"
94
+ end
95
+ end
96
+
97
+ class Section
98
+ attr_accessor :index, :title, :content
99
+
100
+ def initialize( index, title, content )
101
+ @index = index
102
+ @title = RedCloth.new( title ).to_html.gsub( %r{</?p>}, "" ) if title
103
+ @content = RedCloth.new( content || "" )
104
+ end
105
+ end
106
+
107
+ YAML.add_private_type( 'file' ) { |type_id, value| File.read( value ) rescue "" }
108
+ YAML.add_private_type( 'eval' ) { |type_id, value| eval( value ) }
109
+
110
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'manual' ) do |taguri, val|
111
+ index = 0
112
+
113
+ val['chapters'].collect! do |chapter|
114
+ index += 1
115
+ Chapter.new( index, chapter.keys.first, chapter.values.first )
116
+ end
117
+
118
+ YAML.object_maker( Manual, val )
119
+ end
120
+
121
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'meta' ) do |taguri, val|
122
+ YAML.object_maker( Meta, val )
123
+ end
124
+
125
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'product' ) do |taguri, val|
126
+ version = val["version"]
127
+ if version.respond_to?( :type_id )
128
+ if version.type_id == "version"
129
+ if version.value =~ %r{(.*)/(.*)}
130
+ require_file, constant = $1, $2
131
+ else
132
+ constant = version.value
133
+ end
134
+
135
+ require require_file if require_file
136
+ val["version"] = eval(constant)
137
+ else
138
+ raise "Unexpected type: #{val.type_id}"
139
+ end
140
+ end
141
+ YAML.object_maker( Product, val )
142
+ end
143
+
144
+ YAML.add_domain_type( 'jamisbuck.org,2004', 'sidebar' ) do |taguri, val|
145
+ YAML.object_maker( Sidebar,
146
+ 'title' => val.keys.first,
147
+ 'content'=> RedCloth.new( val.values.first ) )
148
+ end
149
+
150
+ end
151
+ end
152
+
153
+ if __FILE__ == $0
154
+
155
+ def log_action( action )
156
+ $stderr.puts action
157
+ end
158
+
159
+ unless ( output_path = ARGV[0] )
160
+ $stderr.puts "Usage: #{$0} [/path/to/save/html]"
161
+ exit
162
+ end
163
+
164
+ Dir.mkdir output_path rescue nil
165
+
166
+ log_action "Loading manual.yml..."
167
+ manual = Needle::Manual::Manual.load( 'manual.yml' )
168
+
169
+ # force these to be defined at the TOPLEVEL_BINDING
170
+ object = nil
171
+ guts = nil
172
+
173
+ page = File.open( "page.erb" ) { |file| ERB.new( file.read ) }
174
+ page.filename = "page.erb"
175
+
176
+ template = File.open( "index.erb" ) { |file| ERB.new( file.read ) }
177
+ template.filename = "index.erb"
178
+
179
+ File.open( File.join( output_path, "index.html" ), "w" ) do |file|
180
+ guts = template.result
181
+ file << page.result
182
+ end
183
+
184
+ template = File.open( "chapter.erb" ) { |file| ERB.new( file.read ) }
185
+ template.filename = "chapter.erb"
186
+
187
+ manual.chapters.each do |object|
188
+ log_action "Processing chapter ##{object.index}..."
189
+ File.open( File.join( output_path, "chapter-#{object.index}.html" ), "w" ) do |file|
190
+ guts = template.result
191
+ file << page.result
192
+ end
193
+ end
194
+
195
+ log_action "Copying style sheets..."
196
+ FileUtils.cp Dir["*.css"], output_path
197
+
198
+ log_action "Copying images..."
199
+ FileUtils.cp Dir["img/*.jpg"], output_path
200
+
201
+ log_action "Done!"
202
+ end
@@ -0,0 +1,45 @@
1
+ --- !jamisbuck.org,2004/^manual
2
+
3
+ # This content is made available under the Attribution-ShareAlike 2.0
4
+ # license from the Create Commons:
5
+ #
6
+ # http://creativecommons.org/licenses/by-sa/2.0/
7
+
8
+ meta: !^meta
9
+ copyright: 2004
10
+ author: Jamis Buck
11
+ email: jgb3@email.byu.edu
12
+
13
+ product: !^product
14
+ name: Needle-Extras
15
+ tagline: for all your needle needs
16
+ version: !!eval require "../../lib/needle/extras/version"; Needle::Extras::Version::STRING
17
+ urls:
18
+ - Project Page: http://rubyforge.org/projects/needle
19
+ - User Manual: http://needle.rubyforge.org/extras
20
+ - API Documentation: http://needle.rubyforge.org/extras/api
21
+ - FAQ Document: http://needle.rubyforge.org/extras/faq.html
22
+ - Needle Wiki: http://needle.rubyforge.org/wiki/wiki.pl
23
+
24
+ recent_updates:
25
+ - First Draft
26
+
27
+ chapters:
28
+
29
+ - Introduction:
30
+ - What is Needle-Extras?: !!file parts/intro_what_is_extras.txt
31
+ - How Do I Use It?: !!file parts/intro_usage.txt
32
+ - License Information: !!file parts/intro_license.txt
33
+ - Support: !!file parts/intro_support.txt
34
+
35
+ - AttrInject:
36
+ - Overview: !!file parts/attrinject_overview.txt
37
+ - Usage: !!file parts/attrinject_usage.txt
38
+
39
+ - Multicast:
40
+ - Overview: !!file parts/multicast_overview.txt
41
+ - Usage: !!file parts/multicast_usage.txt
42
+
43
+ - RequireLibrary:
44
+ - Overview: !!file parts/requirelibrary_overview.txt
45
+ - Usage: !!file parts/requirelibrary_usage.txt
@@ -0,0 +1,71 @@
1
+ <html>
2
+ <head>
3
+ <title><%= manual.product.name %> Manual<% if object %> :: <%= object.page_title %><% end %></title>
4
+ <link type="text/css" rel="stylesheet" href="manual.css" />
5
+ </head>
6
+
7
+ <body>
8
+ <div id="banner">
9
+ <table border='0' cellpadding='0' cellspacing='0' width='100%'>
10
+ <tr><td valign='top' align='left'>
11
+ <div class="title">
12
+ <span class="product"><%= manual.product.name %>&mdash;</span><br />
13
+ <span class="tagline"><%= manual.product.tagline %></span>
14
+ </div>
15
+ </td><td valign='middle' align='right'>
16
+ <div class="info">
17
+ <%= manual.product.name %> Version: <strong><%= manual.product.version %></strong><br />
18
+ Manual Last Updated: <strong><%= Time.now.gmtime.strftime('%Y-%m-%d %H:%M %Z') %></strong>
19
+ </div>
20
+ </td></tr>
21
+ </table>
22
+ </div>
23
+
24
+ <table border='0' width='100%' cellpadding='0' cellspacing='0'>
25
+ <tr><td valign='top'>
26
+
27
+ <div id="navigation">
28
+ <h1><%= manual.product.name %> Manual</h1>
29
+
30
+ <h2>Chapters</h2>
31
+ <ol type="I">
32
+ <% manual.chapters.each do |c| %>
33
+ <li><%= "<strong>" if c == object %>
34
+ <a href="chapter-<%= c.index %>.html">
35
+ <%= c.title %>
36
+ </a>
37
+ <%= "</strong> <big>&larr;</big>" if c == object %>
38
+ <ol type="1">
39
+ <% c.sections.each do |s|
40
+ next unless s.title %>
41
+ <li><a href="chapter-<%= c.index %>.html#s<%= s.index %>"><%= s.title %></a></li>
42
+ <% end %>
43
+ </ol>
44
+ </li>
45
+ <% end %>
46
+ </ol>
47
+
48
+ <h2>Other Documentation</h2>
49
+
50
+ <ul>
51
+ <li><a href="http://needle.rubyforge.org/extras/api/index.html">Needle-Extras API</a></li>
52
+ </ul>
53
+
54
+ <div class="license">
55
+ <a href="http://creativecommons.org/licenses/by-sa/2.0/"><img alt="Creative Commons License" border="0" src="http://creativecommons.org/images/public/somerights" /></a><br />
56
+ This manual is licensed under a <a href="http://creativecommons.org/licenses/by-sa/2.0/">Creative Commons License</a>.
57
+ </div>
58
+ </div>
59
+
60
+ </td><td valign='top' width="100%">
61
+
62
+ <div id="content">
63
+
64
+ <%= guts %>
65
+
66
+ </div>
67
+
68
+ </td></tr>
69
+ </table>
70
+ </body>
71
+ </html>
@@ -0,0 +1 @@
1
+ AttrInject is an implementation of dependency injection that uses declared interfaces to determine dependencies. It is based on an implementation by Christian Neukirchen at "http://rafb.net/paste/results/sexpfu84.html":http://rafb.net/paste/results/sexpfu84.html.
@@ -0,0 +1,34 @@
1
+ AttrInject is very straightforward to use. Just require the AttrInject library in every service implementation and use the new @attr_inject@ macro to specify which other services the class depends on:
2
+
3
+ <pre>
4
+ require 'needle/extras/attr-inject'
5
+
6
+ class Foo
7
+ attr_inject :bar
8
+ attr_inject :baz, :blah
9
+
10
+ def frobnicate
11
+ @bar + @baz / @blah
12
+ end
13
+ end
14
+ </pre>
15
+
16
+ The @attr_inject@ macro does not create any accessors--it only declares the dependencies that the corresponding service has. Then, when you register the service, you specify one of the @inject@ service models:
17
+
18
+ <pre>
19
+ require 'needle'
20
+ require 'needle/extras'
21
+ ...
22
+ reg.require_library 'needle/extras'
23
+ reg.define do |b|
24
+ b.bar { 5 }
25
+ b.baz { 10 }
26
+ b.blah { Math::PI }
27
+
28
+ b.foo( :model => :singleton_inject ) { Foo.new }
29
+ end
30
+ </pre>
31
+
32
+ The @singleton_inject@ service model is just like @singleton@, but it will also automatically inject all of the declared dependencies into the new service. Thus, invoking @#frobnicate@ on the @foo@ service would compute and return (in this case) @5 + 10 / PI@.
33
+
34
+ This approach has the benefit of reducing the amount of initialization code you have to write. On the other hand, it more tightly couples your implementation code to Needle itself.
@@ -0,0 +1,5 @@
1
+ Needle-Extras is made available under either the BSD license, or the same license Ruby (which, by extension, also allows the GPL as a permissable license as well). You can view the full text of any of these licenses in the @doc@ subdirectory of the Needle distrubtion. The texts of the BSD and GPL licenses are also available online: "BSD":http://www.opensource.org/licenses/bsd-license.php and "GPL":http://www.opensource.org/licenses/gpl-license.php.
2
+
3
+ This manual (in any form, be it source or otherwise) and the scripts and templates used to generate it, are all distributed under the "Creative Commons":http://creativecommons.org "Attribution-ShareAlike":http://creativecommons.org/licenses/by-sa/2.0 license.
4
+
5
+ If you desire permission to use either Needle-Extras or the manual in a manner incompatible with these licenses, please contact the copyright holder ("Jamis Buck":mailto:jgb3@email.byu.edu) in order to negotiate a more compatible license.
@@ -0,0 +1 @@
1
+ Mailing lists, bug trackers, feature requests, and public forums are available (courtesy of "RubyForge":http://rubyforge.org) at Needle's RubyForge project page. Just direct your browser to "http://rubyforge.org/projects/needle":http://rubyforge.org/projects/needle.
@@ -0,0 +1,30 @@
1
+ You can make all of the services in Needle-Extras available to your applications in either of two ways.
2
+
3
+ The first way uses the standard @Collection#require@ method to load a service library and include it in a container:
4
+
5
+ <pre>
6
+ require 'needle'
7
+
8
+ reg = Needle::Registry.new
9
+ reg.require 'needle/extras', 'Needle::Extras'
10
+ ...
11
+ </pre>
12
+
13
+ The second way uses the RequireLibrary framework provided by Needle-Extras itself:
14
+
15
+ <pre>
16
+ require 'needle'
17
+ require 'needle/extras'
18
+
19
+ reg = Needle::Registry.new
20
+ reg.require_library 'needle/extras'
21
+ </pre>
22
+
23
+ If you don't want to use _all_ of the services available in Needle-Extras, you can include just the ones you want by requiring them directly:
24
+
25
+ <pre>
26
+ require 'needle'
27
+
28
+ reg = Needle::Registry.new
29
+ reg.require 'needle/extras/multicast', 'Needle::Extras::Multicast'
30
+ </pre>
@@ -0,0 +1,7 @@
1
+ Needle-Extras is a collection of additional services and utilities that can be used in conjuction with Needle. It is a kind of test-bed for services that may eventually find their way into Needle itself.
2
+
3
+ Currently, Needle-Extras includes the following services and utilities:
4
+
5
+ * AttrInject: this is an implementation of simple interface-based dependency injection, inspired by a proof-of-concept by Christian Neukirchen ("http://rafb.net/paste/results/sexpfu84.html":http://rafb.net/paste/results/sexpfu84.html).
6
+ * Multicast: this is a demonstration of how to do multicast services--services that delegate the messages they receive to a collection of other services.
7
+ * RequireLibrary: this is not actually a service, so much as a mini-framework for simplifying the process of including a third-party service in your application.
@@ -0,0 +1 @@
1
+ The multicast service allows you to easily broadcast messages to a specified set of objects. It is, in essence, a kind of observer pattern, with t he observers being given to the multicaster when it is created. Events are then sent to the observers by invoking methods on the multicaster.
@@ -0,0 +1,22 @@
1
+ The multicast service is parameterized. Just send it a list of objects (i.e., other services) that you want multicasted to, and it will return a new multicaster object.
2
+
3
+ <pre>
4
+ reg = Needle::Registry.define do |b|
5
+ b.require 'needle/extras/multicast', 'Needle::Extras::Multicast'
6
+
7
+ b.foo { "hello" }
8
+ b.bar { [ 1, 2, 3 ] }
9
+ b.baz { "test" }
10
+
11
+ b.multicaster { |c,| c.multicast c.foo, c.bar, c.baz }
12
+ end
13
+ </pre>
14
+
15
+ Once you've registered your service, you can send messages to the observing services by sending messages to the multicaster:
16
+
17
+ <pre>
18
+ m = reg.multicaster
19
+ p m.length #-> [ 5, 3, 4 ]
20
+ </pre>
21
+
22
+ The multicaster will return an array of the return values of all of the observing services. Thus, in the above example, an array of the lengths of each of the @foo@, @bar@, and @baz@ services is returned.
@@ -0,0 +1,5 @@
1
+ RequireLibrary is not a service--it is a mini-framework for registering service libraries with Needle so that they can be imported into other projects with a minimum of headache.
2
+
3
+ Currently, Needle supports @Container#require@ as the library import mechanism. This requires you to specify both the file containing the service registration method, as well as the Module that contains the method.
4
+
5
+ RequireLibrary takes some of the duplication out of the process by allowing application developers to register a callback hook with Needle, which will be invoked when the new @Container#require_library@ method is invoked.
@@ -0,0 +1,34 @@
1
+ For developers of service libraries, RequireLibrary provides a hook for registering their libraries with Needle:
2
+
3
+ <pre>
4
+ module Foo
5
+ module Bar
6
+
7
+ def register_services( container )
8
+ ...
9
+ end
10
+ module_function :register_services
11
+
12
+ if defined?(Needle.register_library)
13
+ Needle.register_library( 'foo/bar', self )
14
+ end
15
+
16
+ end
17
+ end
18
+ </pre>
19
+
20
+ The @#register_services@ method is Needle's standard callback for registering a library's services with the given container.
21
+
22
+ The next lines, though, check to see if @Needle.register_library@ is defined. This allows the library to be used even when Needle-Extras is not loaded, or even installed. If the method exists, it is invoked with the @require@ path of the file, and the module reference that contains the @#register_services@ method.
23
+
24
+ Then, consumers of the library can load it using RequireLibrary as follows:
25
+
26
+ <pre>
27
+ require 'needle'
28
+
29
+ reg = Needle::Registry.new
30
+ reg.require_library 'foo/bar'
31
+ ...
32
+ </pre>
33
+
34
+ The call to @Container#require_library@ invokes @Kernel#require@, and then looks to see if there is a hook registered for the @'foo/bar'@ path. If there is, the hook is invoked, which (by default) invokes the @#register_services@ method, passing the current container as the parameter.