dao 5.5.0 → 8.0.0

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