dao 5.5.0 → 8.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.
Files changed (52) hide show
  1. checksums.yaml +6 -14
  2. data/README.md +258 -0
  3. data/Rakefile +4 -5
  4. data/coerce-0.0.8/README +28 -0
  5. data/coerce-0.0.8/Rakefile +392 -0
  6. data/coerce-0.0.8/coerce.gemspec +31 -0
  7. data/coerce-0.0.8/lib/coerce.rb +210 -0
  8. data/dao.gemspec +38 -25
  9. data/lib/dao.rb +18 -81
  10. data/lib/dao/_lib.rb +42 -0
  11. data/lib/dao/active_record.rb +8 -8
  12. data/lib/dao/api/call.rb +19 -4
  13. data/lib/dao/api/dsl.rb +1 -1
  14. data/lib/dao/coerce.rb +211 -0
  15. data/lib/dao/conducer.rb +10 -14
  16. data/lib/dao/conducer/controller_support.rb +5 -0
  17. data/lib/dao/conducer/view_support.rb +0 -2
  18. data/lib/dao/db.rb +0 -1
  19. data/lib/dao/errors.rb +17 -11
  20. data/lib/dao/errors2html.rb +128 -0
  21. data/lib/dao/form.rb +13 -16
  22. data/lib/dao/messages.rb +0 -4
  23. data/lib/dao/path.rb +1 -1
  24. data/lib/dao/route.rb +2 -2
  25. data/lib/dao/status.rb +3 -4
  26. data/lib/dao/support.rb +26 -19
  27. data/lib/dao/upload.rb +0 -1
  28. data/lib/dao/validations/common.rb +6 -6
  29. data/lib/dao/validations/validator.rb +3 -3
  30. data/lib/dao/wrap.rb +259 -0
  31. data/tasks/default.rake +207 -0
  32. data/tasks/this.rb +207 -0
  33. data/test/active_model_conducer_lint_test.rb +3 -11
  34. data/test/api_test.rb +24 -35
  35. data/test/conducer_test.rb +37 -47
  36. data/test/errors_test.rb +29 -13
  37. data/test/form_test.rb +24 -34
  38. data/test/rake_rerun_reporter.rb +74 -0
  39. data/test/support_test.rb +9 -14
  40. data/test/test_helper.rb +220 -0
  41. data/test/{helper.rb → util.rb} +0 -0
  42. data/test/validations_test.rb +14 -28
  43. data/wrap-1.5.2/README +57 -0
  44. data/wrap-1.5.2/Rakefile +394 -0
  45. data/wrap-1.5.2/lib/wrap.rb +295 -0
  46. data/{test → wrap-1.5.2/test}/testing.rb +0 -1
  47. data/wrap-1.5.2/test/wrap_test.rb +397 -0
  48. data/wrap-1.5.2/wrap.gemspec +38 -0
  49. metadata +47 -103
  50. data/Gemfile +0 -16
  51. data/Gemfile.lock +0 -118
  52. data/README +0 -256
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ODJiYmNmMGY2MDdkYjM1MThiOTUyOGFkMzc5ZDkwMTNhYjA1YjNhYw==
5
- data.tar.gz: !binary |-
6
- YzRmOWI5Y2ViYjM0NjM5MWFiMDYzZjFhMjEwYjNkMjJkMTJlNmY1Mw==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- YzU1M2Y0NWM5MGU2OWI0NTU5MGFlOGQzZWI0MjRkNzEwZTllZGRmMDJkOGNk
10
- ZjhlNjAzZjc4OWFmM2M5YjhmMTNlNWVhMWMxNWFhOTgwZTQ4MDdlYTg1MGY0
11
- YmY5NWI5N2E0NjYzZmM4NjA5ZDYwNWYyYmI5Zjk4NWJiZTNiOTg=
12
- data.tar.gz: !binary |-
13
- YTc5NDY5OTBlYjA1MWRjZjA3MjI5MzliOTlhNzQ1MTljZjc3NGJiMjFhY2Y0
14
- ZjJjYmQxN2RmZmViMDEzMjlhOWE5ZDEwODVlOTUwMmUwMGE3ZWZiMzEwZDkw
15
- ZjdlNGFkMjJmN2JmZWZhZTg0N2U1ZDUyMWEwMGY4ZDJmMDJlYjg=
2
+ SHA256:
3
+ metadata.gz: 6a987da50df407863683e68b28b64a48a2e4e825d015e5c62a3dc027f456d959
4
+ data.tar.gz: b4a2c2ede251574cb1224b75fc0532bdeb761d4597b7ffdc0af460e1ee4b904b
5
+ SHA512:
6
+ metadata.gz: 32740b72cf6a178a8279cd56b3d88c35ee2f1d5f021f544f2e9122d1b7df3f4f70a328eb52f53067a109e788c5b39329b2a3f28d9bb61809a3526f61b4341ea8
7
+ data.tar.gz: 678ee2e96b9e1a112b29e96eb690c2cff992dabe1b2a70629b99dae052b2ab2893574e14acf14ff79f7aa90f0fc1110d83982e725e28137fc5cbac8cdf8c1278
@@ -0,0 +1,258 @@
1
+ # dao
2
+
3
+ ## SYNOPSIS
4
+
5
+ a sa-weet-ass library for structuring rails applications using the 'data
6
+ access object' design pattern. dao consists of two main data access
7
+ objects, *api* objects and *conducer* objects. conducers combine the
8
+ presenter pattern with the conductor pattern.
9
+
10
+
11
+ ### API
12
+
13
+ class Api < Dao::Api
14
+ call('/posts') do
15
+ get do
16
+ data[:posts] = Post.all.map{|post| post.attributes}
17
+ end
18
+
19
+ post do
20
+ post = Post.new(params[:post])
21
+
22
+ if post.save
23
+ data[:post] = post.attributes
24
+ else
25
+ status 420
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ ### CONDUCER
32
+
33
+ * TODO
34
+
35
+ wikipedia has this to say about dao in general
36
+
37
+ >
38
+ > In computer software, a data access object (DAO) is an object that
39
+ > provides an abstract interface to some type of database or other
40
+ > persistence mechanism. By mapping application calls to the persistence
41
+ > layer, DAOs provide some specific data operations without exposing
42
+ > details of the database. This isolation supports the single
43
+ > responsibility principle. It separates what data accesses the
44
+ > application needs, in terms of domain-specific objects and data types
45
+ > (the public interface of the DAO), from how these needs can be satisfied
46
+ > with a specific DBMS, database schema, etc. (the implementation of the
47
+ > DAO).
48
+ >
49
+ -- http://en.wikipedia.org/wiki/Data_access_object
50
+
51
+ and this to say about the single responsibility principle
52
+
53
+ >
54
+ > In object-oriented programming, the single responsibility principle
55
+ > states that every class should have a single responsibility, and that
56
+ > responsibility should be entirely encapsulated by the class. All its
57
+ > services should be narrowly aligned with that responsibility.
58
+
59
+ > Responsibility [is defined] as a reason to change, and [single
60
+ > responsibility means] that a class or module should have one, and only
61
+ > one, reason to change. As an example, consider a module that compiles and
62
+ > prints a report. Such a module can be changed for two reasons. First,
63
+ > the content of the report can change. Second, the format of the report
64
+ > can change. These two things change for very different causes; one
65
+ > substantive, and one cosmetic. The single responsibility principle says
66
+ > that these two aspects of the problem are really two separate
67
+ > responsibilities, and should therefore be in separate classes or
68
+ > modules. It would be a bad design to couple two things that change for
69
+ > different reasons at different times.
70
+ >
71
+ -- http://en.wikipedia.org/wiki/Single_responsibility_principle
72
+
73
+ even though rails is the sweet, its ActiveRecord class violates (or, at
74
+ least, encourages a programmer to violate) the single responsibility
75
+ principle
76
+
77
+ this leads to obvious problems
78
+
79
+ >
80
+ > Jim Weirich, at the end of his SOLID Ruby Talk at the 2009 Ruby
81
+ > Conference, asks the audience: "ActiveRecord objects implement a domain
82
+ > concept and a persistence concept. Does this violate the SRP (Single
83
+ > Responsibility Principle)?" The audience agrees that it does violate the
84
+ > SRP. Jim asks if this bothers them. Many members of the audience say
85
+ > yes. Why? It makes testing harder. It makes the persistence object a lot
86
+ > heavier.
87
+ >
88
+ -- http://programmers.stackexchange.com/questions/119352/does-the-activerecord-pattern-follow-encourage-the-solid-design-principles#comment293734_119352
89
+
90
+ and subtle yet sweeping consequences (as described by uncle bob)
91
+
92
+ >
93
+ > The problem I have with ActiveRecord is that it creates confusion about
94
+ > ... two very different styles of programming. A database table is a
95
+ > data structure. It has exposed data and no behavior. But an ActiveRecord
96
+ > appears to be an object. It has “hidden” data, and exposed behavior. I
97
+ > put the word “hidden” in quotes because the data is, in fact, not
98
+ > hidden. Almost all ActiveRecord derivatives export the database columns
99
+ > through accessors and mutators. Indeed, the Active Record is meant to be
100
+ > used like a data structure.
101
+
102
+ > On the other hand, many people put business rule methods in their
103
+ > ActiveRecord classes; which makes them appear to be objects. This leads
104
+ > to a dilemma. On which side of the line does the Active Record really
105
+ > fall? Is it an object? Or is it a data structure?
106
+
107
+ > This dilemma is the basis for the oft-cited impedance mismatch between
108
+ > relational databases and object oriented languages. Tables are data
109
+ > structures, not classes. Objects are encapsulated behavior, not database
110
+ > rows.
111
+
112
+ > ...
113
+
114
+ > The problem is that Active Records are data structures. Putting business
115
+ > rule methods in them doesn’t turn them into true objects. In the end,
116
+ > the algorithms that employ ActiveRecords are vulnerable to changes in
117
+ > schema, and changes in type. They are not immune to changes in type, the
118
+ > way algorithms that use objects are.
119
+
120
+ > ...
121
+
122
+ > So applications built around ActiveRecord are applications built around
123
+ > data structures. And applications that are built around data structures
124
+ > are procedural—they are not object oriented. The opportunity we miss
125
+ > when we structure our applications around ActiveRecord is the
126
+ > opportunity to use object oriented design.
127
+ >
128
+ -- https://sites.google.com/site/unclebobconsultingllc/active-record-vs-objects
129
+
130
+ and a clear solution (again, uncle bob)
131
+
132
+ > I am not recommending against the use of ActiveRecord. I think the
133
+ > pattern is very useful. What I am advocating is a separation between the
134
+ > application and ActiveRecord.
135
+
136
+ > ActiveRecord belongs in the layer that separates the database from the
137
+ > application. It makes a very convenient halfway-house between the hard
138
+ > data structures of database tables, and the behavior exposing objects in
139
+ > the application.
140
+
141
+ > Applications should be designed and structured around objects, not data
142
+ > structures. Those objects should expose business behaviors, and hide any
143
+ > vestige of the database.
144
+ >
145
+ -- https://sites.google.com/site/unclebobconsultingllc/active-record-vs-objects
146
+
147
+ welcome to the dao
148
+
149
+
150
+ ## DESCRIPTION
151
+
152
+ ### API
153
+
154
+ applications that are written on dao look like this in ruby
155
+
156
+ result = api.call('/posts/new', params)
157
+
158
+ and like this in javascript
159
+
160
+ result = api.call('/posts/new', params)
161
+
162
+ in command-line applications they look like this
163
+
164
+ result = api.call('/posts/new', params)
165
+
166
+ and in tests this syntax is used
167
+
168
+ result = api.call('/posts/new', params)
169
+
170
+ when a developer wants to understand the interface of a dao application she does
171
+ this
172
+
173
+ vi app/api.rb
174
+
175
+ when a developer of a dao application wants to play with a dao application
176
+ interactively she does
177
+
178
+ (rails console)
179
+
180
+ > api = Api.new result = api.call('/posts/new', params)
181
+
182
+ when a remote client wants to understand the api of a dao application she
183
+ does
184
+
185
+ curl --silent http://dao.app.com/api | less
186
+
187
+
188
+
189
+ this kind of brutally consistent interface is made possible by structuring
190
+ access to data around the finest data structure of all time - the hash.
191
+ in the case of dao the hash is a well structured and slightly clever hash,
192
+ but a simple hash interface is the basis of every bit of goodness dao has
193
+ to offer.
194
+
195
+ in dao, application developers do not bring models into controllers and,
196
+ especially not into views. instead, a unified interface to application
197
+ logic and data is used everywhere: in tests, in controllers, from the
198
+ command-line, and also from javascript.
199
+
200
+ this seperation of concerns brings with it many, many desirable qualities:
201
+
202
+ - total seperation of concerns between the front and back end of a web
203
+ application. when developers are using dao changes to the data model
204
+ have zero effect on controllers and views.
205
+
206
+ - issues related to having models in controllers and views such as
207
+ difficulty reasoning about caching and n+1 queries in views killing
208
+ the db simply disappear.
209
+
210
+ - bad programming practices like using quasi-global variables
211
+ (current_user) or decorating models with view specific attributes
212
+ (password_verification) are no longer needed.
213
+
214
+ - developers are able to reason over the abilities of an application by
215
+ reading only a few source files.
216
+
217
+ - databases can be swapped, mixed, or alternate storage/caching
218
+ mechanisms added at any time without affecting the application's
219
+ controllers or views.
220
+
221
+ - transition from form based views to semi-ajax ones to fully-ajax ones
222
+ is direct.
223
+
224
+ - forms and interfaces that involve dozens of models are as easy to deal
225
+ with as simple ones.
226
+
227
+ - code can be optimized at the interface.
228
+
229
+ ## READING
230
+
231
+ * http://blog.plataformatec.com.br/2012/03/barebone-models-to-use-with-actionpack-in-rails-4-0/
232
+ * http://martinfowler.com/eaaCatalog/serviceLayer.html
233
+ * http://blog.firsthand.ca/2011/10/rails-is-not-your-application.html
234
+ * http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/dao.html
235
+ * http://www.codefutures.com/data-access-object/
236
+ * http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
237
+ * http://www.paperplanes.de/2010/5/7/activerecord_callbacks_ruined_my_life.html
238
+ * http://google-styleguide.googlecode.com/svn/trunk/jsoncstyleguide.xml
239
+ * http://pragdave.blogs.pragprog.com/pragdave/2007/03/the_radar_archi.html
240
+ * http://borisstaal.com/post/22586260753/mvc-in-a-browser-vs-reality
241
+
242
+
243
+ ## INSTALL
244
+
245
+ gem 'dao', :path => File.expand_path('..') ### Gemfile
246
+ rails generate dao api
247
+ vim -o app/api.rb app/controllers/api_controller.rb
248
+ curl --silent http://0.0.0.0:3000/api
249
+ curl --silent http://0.0.0.0:3000/api/ping
250
+
251
+ ## HISTORY
252
+
253
+ ### 4.0.0
254
+ - dao depends has tied itself to rails, for better or worse...
255
+ - drop custom form encoding. just use a rack-like approach.
256
+ - dao form parameter encoding has changed slightly to 'dao[/api/path][x,y,z]=42'
257
+ - dao form paramters are now preparsed in a before filter
258
+
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- This.rubyforge_project = 'codeforpeople'
1
+ #This.rubyforge_project = 'codeforpeople'
2
2
  This.author = "Ara T. Howard"
3
3
  This.email = "ara.t.howard@gmail.com"
4
4
  This.homepage = "https://github.com/ahoward/#{ This.lib }"
5
5
 
6
6
  task :license do
7
- open('LICENSE', 'w'){|fd| fd.puts "same as ruby's"}
7
+ open('LICENSE', 'w'){|fd| fd.puts "Ruby"}
8
8
  end
9
9
 
10
10
  task :default do
@@ -93,7 +93,7 @@ task :gemspec do
93
93
  test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
94
94
  summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
95
95
  description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
96
- license = object.respond_to?(:license) ? object.license : "same as ruby's"
96
+ license = object.respond_to?(:license) ? object.license : "Ruby"
97
97
 
98
98
  if This.extensions.nil?
99
99
  This.extensions = []
@@ -145,7 +145,6 @@ task :gemspec do
145
145
 
146
146
  spec.extensions.push(*<%= extensions.inspect %>)
147
147
 
148
- spec.rubyforge_project = <%= This.rubyforge_project.inspect %>
149
148
  spec.author = <%= This.author.inspect %>
150
149
  spec.email = <%= This.email.inspect %>
151
150
  spec.homepage = <%= This.homepage.inspect %>
@@ -297,7 +296,7 @@ BEGIN {
297
296
 
298
297
  # discover full path to this ruby executable
299
298
  #
300
- c = Config::CONFIG
299
+ c = RbConfig::CONFIG
301
300
  bindir = c["bindir"] || c['BINDIR']
302
301
  ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
303
302
  ruby_ext = c['EXEEXT'] || ''
@@ -0,0 +1,28 @@
1
+ NAME
2
+ coerce
3
+
4
+ DESCRIPTION
5
+ a ruby library full of common cast/coercion operations
6
+
7
+ INSTALL
8
+ gem install coerce
9
+
10
+ USAGE
11
+
12
+ :~/git/coerce $ irb -r lib/coerce.rb
13
+
14
+ ruby-1.8.7-p330 :001 > Coerce.integer('42')
15
+ => 42
16
+
17
+ ruby-1.8.7-p330 :002 > Coerce.number('42.0')
18
+ => 42.0
19
+
20
+ ruby-1.8.7-p330 :003 > Coerce.time('yesterday')
21
+ => Wed Aug 24 12:00:00 -0600 2011
22
+
23
+ ruby-1.8.7-p330 :004 > Coerce.list_of_times('yesterday, today')
24
+ => [Wed Aug 24 12:00:00 -0600 2011, Thu Aug 25 19:30:00 -0600 2011]
25
+
26
+ ruby-1.8.7-p330 :005 > Coerce.list_of_floats("42, 4.2\n\n 420")
27
+ => [42.0, 4.2, 420.0]
28
+
@@ -0,0 +1,392 @@
1
+ This.rubyforge_project = 'codeforpeople'
2
+ This.author = "Ara T. Howard"
3
+ This.email = "ara.t.howard@gmail.com"
4
+ This.homepage = "https://github.com/ahoward/#{ This.lib }"
5
+
6
+
7
+ task :default do
8
+ puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
9
+ end
10
+
11
+ task :test do
12
+ run_tests!
13
+ end
14
+
15
+ namespace :test do
16
+ task(:unit){ run_tests!(:unit) }
17
+ task(:functional){ run_tests!(:functional) }
18
+ task(:integration){ run_tests!(:integration) }
19
+ end
20
+
21
+ def run_tests!(which = nil)
22
+ which ||= '**'
23
+ test_dir = File.join(This.dir, "test")
24
+ test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
25
+ test_rbs = Dir.glob(test_glob).sort
26
+
27
+ div = ('=' * 119)
28
+ line = ('-' * 119)
29
+
30
+ test_rbs.each_with_index do |test_rb, index|
31
+ testno = index + 1
32
+ command = "#{ This.ruby } -I ./lib -I ./test/lib #{ test_rb }"
33
+
34
+ puts
35
+ say(div, :color => :cyan, :bold => true)
36
+ say("@#{ testno } => ", :bold => true, :method => :print)
37
+ say(command, :color => :cyan, :bold => true)
38
+ say(line, :color => :cyan, :bold => true)
39
+
40
+ system(command)
41
+
42
+ say(line, :color => :cyan, :bold => true)
43
+
44
+ status = $?.exitstatus
45
+
46
+ if status.zero?
47
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
48
+ say("SUCCESS", :color => :green, :bold => true)
49
+ else
50
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
51
+ say("FAILURE", :color => :red, :bold => true)
52
+ end
53
+ say(line, :color => :cyan, :bold => true)
54
+
55
+ exit(status) unless status.zero?
56
+ end
57
+ end
58
+
59
+
60
+ task :gemspec do
61
+ ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem']
62
+ ignore_directories = ['pkg']
63
+ ignore_files = ['test/log', 'a.rb'] + Dir['db/*'] + %w'db'
64
+
65
+ shiteless =
66
+ lambda do |list|
67
+ list.delete_if do |entry|
68
+ next unless test(?e, entry)
69
+ extension = File.basename(entry).split(%r/[.]/).last
70
+ ignore_extensions.any?{|ext| ext === extension}
71
+ end
72
+ list.delete_if do |entry|
73
+ next unless test(?d, entry)
74
+ dirname = File.expand_path(entry)
75
+ ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
76
+ end
77
+ list.delete_if do |entry|
78
+ next unless test(?f, entry)
79
+ filename = File.expand_path(entry)
80
+ ignore_files.any?{|file| File.expand_path(file) == filename}
81
+ end
82
+ end
83
+
84
+ lib = This.lib
85
+ object = This.object
86
+ version = This.version
87
+ files = shiteless[Dir::glob("**/**")]
88
+ executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
89
+ #has_rdoc = true #File.exist?('doc')
90
+ test_files = test(?e, "test/#{ lib }.rb") ? "test/#{ lib }.rb" : nil
91
+ summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
92
+ description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
93
+ license = object.respond_to?(:license) ? object.license : "same as ruby's"
94
+
95
+ if This.extensions.nil?
96
+ This.extensions = []
97
+ extensions = This.extensions
98
+ %w( Makefile configure extconf.rb ).each do |ext|
99
+ extensions << ext if File.exists?(ext)
100
+ end
101
+ end
102
+ extensions = [extensions].flatten.compact
103
+
104
+ # TODO
105
+ if This.dependencies.nil?
106
+ dependencies = []
107
+ else
108
+ case This.dependencies
109
+ when Hash
110
+ dependencies = This.dependencies.values
111
+ when Array
112
+ dependencies = This.dependencies
113
+ end
114
+ end
115
+
116
+ template =
117
+ if test(?e, 'gemspec.erb')
118
+ Template{ IO.read('gemspec.erb') }
119
+ else
120
+ Template {
121
+ <<-__
122
+ ## <%= lib %>.gemspec
123
+ #
124
+
125
+ Gem::Specification::new do |spec|
126
+ spec.name = <%= lib.inspect %>
127
+ spec.version = <%= version.inspect %>
128
+ spec.platform = Gem::Platform::RUBY
129
+ spec.summary = <%= lib.inspect %>
130
+ spec.description = <%= description.inspect %>
131
+ spec.license = <%= license.inspect %>
132
+
133
+ spec.files =\n<%= files.sort.pretty_inspect %>
134
+ spec.executables = <%= executables.inspect %>
135
+
136
+ spec.require_path = "lib"
137
+
138
+ spec.test_files = <%= test_files.inspect %>
139
+
140
+ <% dependencies.each do |lib_version| %>
141
+ spec.add_dependency(*<%= Array(lib_version).flatten.inspect %>)
142
+ <% end %>
143
+
144
+ spec.extensions.push(*<%= extensions.inspect %>)
145
+
146
+ spec.rubyforge_project = <%= This.rubyforge_project.inspect %>
147
+ spec.author = <%= This.author.inspect %>
148
+ spec.email = <%= This.email.inspect %>
149
+ spec.homepage = <%= This.homepage.inspect %>
150
+ end
151
+ __
152
+ }
153
+ end
154
+
155
+ Fu.mkdir_p(This.pkgdir)
156
+ gemspec = "#{ lib }.gemspec"
157
+ open(gemspec, "w"){|fd| fd.puts(template)}
158
+ This.gemspec = gemspec
159
+ end
160
+
161
+ task :gem => [:clean, :gemspec] do
162
+ Fu.mkdir_p(This.pkgdir)
163
+ before = Dir['*.gem']
164
+ cmd = "gem build #{ This.gemspec }"
165
+ `#{ cmd }`
166
+ after = Dir['*.gem']
167
+ gem = ((after - before).first || after.first) or abort('no gem!')
168
+ Fu.mv(gem, This.pkgdir)
169
+ This.gem = File.join(This.pkgdir, File.basename(gem))
170
+ end
171
+
172
+ task :readme do
173
+ samples = ''
174
+ prompt = '~ > '
175
+ lib = This.lib
176
+ version = This.version
177
+
178
+ Dir['sample*/*'].sort.each do |sample|
179
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
180
+
181
+ cmd = "cat #{ sample }"
182
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
183
+ samples << Util.indent(`#{ cmd }`, 4) << "\n"
184
+
185
+ cmd = "ruby #{ sample }"
186
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
187
+
188
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
189
+ samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
190
+ end
191
+
192
+ template =
193
+ if test(?e, 'readme.erb')
194
+ Template{ IO.read('readme.erb') }
195
+ else
196
+ Template {
197
+ <<-__
198
+ NAME
199
+ #{ lib }
200
+
201
+ DESCRIPTION
202
+
203
+ INSTALL
204
+ gem install #{ lib }
205
+
206
+ SAMPLES
207
+ #{ samples }
208
+ __
209
+ }
210
+ end
211
+
212
+ open("README", "w"){|fd| fd.puts template}
213
+ end
214
+
215
+
216
+ task :clean do
217
+ Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
218
+ end
219
+
220
+
221
+ task :release => [:clean, :gemspec, :gem] do
222
+ gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
223
+ raise "which one? : #{ gems.inspect }" if gems.size > 1
224
+ raise "no gems?" if gems.size < 1
225
+
226
+ cmd = "gem push #{ This.gem }"
227
+ puts cmd
228
+ puts
229
+ system(cmd)
230
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
231
+
232
+ cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
233
+ puts cmd
234
+ puts
235
+ system(cmd)
236
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
237
+ end
238
+
239
+
240
+
241
+
242
+
243
+ BEGIN {
244
+ # support for this rakefile
245
+ #
246
+ $VERBOSE = nil
247
+
248
+ require 'ostruct'
249
+ require 'erb'
250
+ require 'fileutils'
251
+ require 'rbconfig'
252
+ require 'pp'
253
+
254
+ # fu shortcut
255
+ #
256
+ Fu = FileUtils
257
+
258
+ # cache a bunch of stuff about this rakefile/environment
259
+ #
260
+ This = OpenStruct.new
261
+
262
+ This.file = File.expand_path(__FILE__)
263
+ This.dir = File.dirname(This.file)
264
+ This.pkgdir = File.join(This.dir, 'pkg')
265
+
266
+ # grok lib
267
+ #
268
+ lib = ENV['LIB']
269
+ unless lib
270
+ lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
271
+ end
272
+ This.lib = lib
273
+
274
+ # grok version
275
+ #
276
+ version = ENV['VERSION']
277
+ unless version
278
+ require "./lib/#{ This.lib }"
279
+ This.name = lib.capitalize
280
+ This.object = eval(This.name)
281
+ version = This.object.send(:version)
282
+ end
283
+ This.version = version
284
+
285
+ # see if dependencies are export by the module
286
+ #
287
+ if This.object.respond_to?(:dependencies)
288
+ This.dependencies = This.object.dependencies
289
+ end
290
+
291
+ # we need to know the name of the lib an it's version
292
+ #
293
+ abort('no lib') unless This.lib
294
+ abort('no version') unless This.version
295
+
296
+ # discover full path to this ruby executable
297
+ #
298
+ c = Config::CONFIG
299
+ bindir = c["bindir"] || c['BINDIR']
300
+ ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
301
+ ruby_ext = c['EXEEXT'] || ''
302
+ ruby = File.join(bindir, (ruby_install_name + ruby_ext))
303
+ This.ruby = ruby
304
+
305
+ # some utils
306
+ #
307
+ module Util
308
+ def indent(s, n = 2)
309
+ s = unindent(s)
310
+ ws = ' ' * n
311
+ s.gsub(%r/^/, ws)
312
+ end
313
+
314
+ def unindent(s)
315
+ indent = nil
316
+ s.each_line do |line|
317
+ next if line =~ %r/^\s*$/
318
+ indent = line[%r/^\s*/] and break
319
+ end
320
+ indent ? s.gsub(%r/^#{ indent }/, "") : s
321
+ end
322
+ extend self
323
+ end
324
+
325
+ # template support
326
+ #
327
+ class Template
328
+ def initialize(&block)
329
+ @block = block
330
+ @template = block.call.to_s
331
+ end
332
+ def expand(b=nil)
333
+ ERB.new(Util.unindent(@template)).result((b||@block).binding)
334
+ end
335
+ alias_method 'to_s', 'expand'
336
+ end
337
+ def Template(*args, &block) Template.new(*args, &block) end
338
+
339
+ # colored console output support
340
+ #
341
+ This.ansi = {
342
+ :clear => "\e[0m",
343
+ :reset => "\e[0m",
344
+ :erase_line => "\e[K",
345
+ :erase_char => "\e[P",
346
+ :bold => "\e[1m",
347
+ :dark => "\e[2m",
348
+ :underline => "\e[4m",
349
+ :underscore => "\e[4m",
350
+ :blink => "\e[5m",
351
+ :reverse => "\e[7m",
352
+ :concealed => "\e[8m",
353
+ :black => "\e[30m",
354
+ :red => "\e[31m",
355
+ :green => "\e[32m",
356
+ :yellow => "\e[33m",
357
+ :blue => "\e[34m",
358
+ :magenta => "\e[35m",
359
+ :cyan => "\e[36m",
360
+ :white => "\e[37m",
361
+ :on_black => "\e[40m",
362
+ :on_red => "\e[41m",
363
+ :on_green => "\e[42m",
364
+ :on_yellow => "\e[43m",
365
+ :on_blue => "\e[44m",
366
+ :on_magenta => "\e[45m",
367
+ :on_cyan => "\e[46m",
368
+ :on_white => "\e[47m"
369
+ }
370
+ def say(phrase, *args)
371
+ options = args.last.is_a?(Hash) ? args.pop : {}
372
+ options[:color] = args.shift.to_s.to_sym unless args.empty?
373
+ keys = options.keys
374
+ keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
375
+
376
+ color = options[:color]
377
+ bold = options.has_key?(:bold)
378
+
379
+ parts = [phrase]
380
+ parts.unshift(This.ansi[color]) if color
381
+ parts.unshift(This.ansi[:bold]) if bold
382
+ parts.push(This.ansi[:clear]) if parts.size > 1
383
+
384
+ method = options[:method] || :puts
385
+
386
+ Kernel.send(method, parts.join)
387
+ end
388
+
389
+ # always run out of the project dir
390
+ #
391
+ Dir.chdir(This.dir)
392
+ }