needle-extras 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.