rid 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +21 -0
  2. data/.gitmodules +3 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +93 -0
  5. data/Rakefile +72 -0
  6. data/bin/rid +7 -0
  7. data/lib/rid.rb +43 -0
  8. data/lib/rid/actions/base.rb +15 -0
  9. data/lib/rid/actions/pull.rb +28 -0
  10. data/lib/rid/actions/push.rb +53 -0
  11. data/lib/rid/actions/routes.rb +41 -0
  12. data/lib/rid/attachments.rb +59 -0
  13. data/lib/rid/commands.rb +41 -0
  14. data/lib/rid/commands/destroy.rb +9 -0
  15. data/lib/rid/commands/generate.rb +9 -0
  16. data/lib/rid/commands/pull.rb +4 -0
  17. data/lib/rid/commands/push.rb +4 -0
  18. data/lib/rid/commands/routes.rb +4 -0
  19. data/lib/rid/design_document.rb +179 -0
  20. data/lib/rid/generators.rb +63 -0
  21. data/lib/rid/generators/application/USAGE +10 -0
  22. data/lib/rid/generators/application/application_generator.rb +51 -0
  23. data/lib/rid/generators/application/templates/README +1 -0
  24. data/lib/rid/generators/application/templates/_attachments/index.html +11 -0
  25. data/lib/rid/generators/application/templates/_attachments/stylesheets/application.css +25 -0
  26. data/lib/rid/generators/application/templates/_id +1 -0
  27. data/lib/rid/generators/application/templates/gitignore +0 -0
  28. data/lib/rid/generators/application/templates/lib/mustache.js +305 -0
  29. data/lib/rid/generators/application/templates/ridrc +1 -0
  30. data/lib/rid/generators/application/templates/validate_doc_update.js +3 -0
  31. data/lib/rid/generators/base.rb +66 -0
  32. data/lib/rid/generators/list/USAGE +8 -0
  33. data/lib/rid/generators/list/list_generator.rb +9 -0
  34. data/lib/rid/generators/list/templates/list.js +29 -0
  35. data/lib/rid/generators/named_base.rb +22 -0
  36. data/lib/rid/generators/scaffold/USAGE +10 -0
  37. data/lib/rid/generators/scaffold/scaffold_generator.rb +28 -0
  38. data/lib/rid/generators/show/USAGE +8 -0
  39. data/lib/rid/generators/show/show_generator.rb +9 -0
  40. data/lib/rid/generators/show/templates/show.js +20 -0
  41. data/lib/rid/generators/validation/USAGE +9 -0
  42. data/lib/rid/generators/validation/templates/validate_doc_update.js +3 -0
  43. data/lib/rid/generators/validation/validation_generator.rb +34 -0
  44. data/lib/rid/generators/view/USAGE +8 -0
  45. data/lib/rid/generators/view/templates/map.js +5 -0
  46. data/lib/rid/generators/view/view_generator.rb +17 -0
  47. data/lib/rid/makros.rb +105 -0
  48. data/lib/rid/version.rb +3 -0
  49. data/rid.gemspec +113 -0
  50. data/spec/rid/design_document_spec.rb +329 -0
  51. data/spec/rid_spec.rb +7 -0
  52. data/spec/spec.opts +1 -0
  53. data/spec/spec_helper.rb +9 -0
  54. metadata +187 -0
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/mustache.js"]
2
+ path = vendor/mustache.js
3
+ url = git://github.com/janl/mustache.js.git
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Johannes J. Schmidt
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,93 @@
1
+ = Rid
2
+ == Red in a different color
3
+
4
+ Standalone Couchdb Application Development Suite
5
+
6
+ With Rid you can easy build a standalone Couchdb application. Rid aims to bring some of the Rails beauty to Couchdb.
7
+ Currently Rid supports Rails style Generators you will love, using the same awesome Thor library used in Rails 3.
8
+
9
+
10
+ == Why?
11
+
12
+ Why do we need a new Couchdb Application Development Suite? We already have the powerful {RidApp}[http://github.com/ridapp/ridapp].
13
+
14
+ I miss some beauty. I miss some elegance on the command line.
15
+
16
+ I know my attempt is almost a small little step towards the beauty I aspire.
17
+ I try to pilfer the most of now: From RidApp and Ruby on Rails.
18
+ So here we go:
19
+
20
+ <em>Rid is designed to structure standalone Couchdb application development for maximum application portability.</em>
21
+
22
+ === Web development that doesn't hurt
23
+
24
+ Rid is a set of open-source tools that's optimized for programmer happiness and sustainable productivity. It lets you write beautyful code by favoring convention over configuration.
25
+
26
+ === Write apps using just Javascript and HTML
27
+
28
+ Render HTML documents using Javascript templates run by Couchdb. You'll get parallelism and cacheability, *using only HTML and Javascript*.
29
+ Building standalone Couchdb applications according to correct principles affords you options not found on other platforms.
30
+
31
+
32
+ == What comes next
33
+
34
+ At the moment, Rid supports generating a scaffold application, pushing to a Couchdb server and pulling from a Couchdb.
35
+ Rid injects the !code and !json makros introduced by RidApp.
36
+
37
+ The next big steps are:
38
+
39
+ * make use of mustache.js in generators
40
+ * build a lightweight Javascript library to assist client side Javascript work
41
+ * enhance generators to build a full flavored scaffold with all CRUD operations
42
+
43
+ == I need your help!
44
+
45
+ The reason why I release that project in such an early state is that I beleave it's you, who invent that desired beauty!
46
+
47
+
48
+ == Prerequisites
49
+
50
+ All you need is Ruby and its packet manager RubyGems. If you do not have a Ruby environment,
51
+
52
+ apt-get install ruby rubygems
53
+
54
+ will probably install the required packages. If you run into any troubles, visit http://docs.rubygems.org/read/chapter/3 for in deapth installation notes for RubyGems, and http://www.ruby-lang.org/en/downloads/ for help on installing Ruby.
55
+
56
+ Of course, Rid relies on Couchdb. You do not have to install your own Couchdb server if you have access to a Couchdb database over the internet, but I recommend it for development.
57
+ If you are new to Couchdb, install it:
58
+
59
+ apt-get install couchdb
60
+
61
+ Take a look at the Couchdb Wiki, where the page http://wiki.apache.org/couchdb/Installation describes the installation for all common systems.
62
+
63
+
64
+ == Installation
65
+
66
+ Rid installation is easy.
67
+ Rid is available as RubyGem, hosted on http://rubygems.org/ (aka Gemcutter) and Rubyforge (http://rubyforge.org).
68
+ Install the gem with
69
+
70
+ gem install rid
71
+
72
+
73
+ == Gettings started
74
+
75
+ Once installed, your system will have the brand new +rid+ command.
76
+ So just type
77
+
78
+ rid
79
+
80
+ and relax.
81
+
82
+
83
+ == Note on Patches/Pull Requests
84
+
85
+ * Fork the project.
86
+ * Make your feature addition or bug fix.
87
+ * Commit, do not mess with rakefile, version, or history.
88
+ * Send me a pull request. Bonus points for topic branches.
89
+
90
+
91
+ == Copyright
92
+
93
+ Copyright (c) 2010 Johannes Jörg Schmidt, TF. See LICENSE for details.
@@ -0,0 +1,72 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require File.join(File.dirname(__FILE__), 'lib', 'rid', 'version')
4
+
5
+ namespace :vendor do
6
+ desc "Update vendor"
7
+ task :update do
8
+ `git submodule update`
9
+ end
10
+
11
+ desc "vendor mustache"
12
+ task :mustache do
13
+ dir = File.expand_path('..', __FILE__)
14
+ source = File.join(dir, 'vendor/mustache.js/mustache.js')
15
+ target = File.join(dir, 'lib/rid/generators/application/templates/lib/')
16
+ FileUtils.cp source, target
17
+ end
18
+ end
19
+
20
+ desc "Vendor all"
21
+ task :vendor => ["vendor:update", "vendor:mustache"]
22
+
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gem|
27
+ gem.name = "rid"
28
+ gem.version = Rid::VERSION
29
+ gem.summary = %Q{Standalone Couchdb Application Development Suite}
30
+ gem.description = %Q{With Couch you can easy build a standalone CouchDB application. Couch aims to bring some of the Rails beauty to CouchDB. Currently Couch supports Rails style Generators you will love, using the same awesome Thor library used in Rails3.}
31
+ gem.email = "schmidt@netzmerk.com"
32
+ gem.homepage = "http://github.com/jo/rid"
33
+ gem.authors = ["Johannes J. Schmidt"]
34
+ gem.rubyforge_project = "rid"
35
+ gem.add_dependency "thor", ">= 0.13.4"
36
+ gem.add_dependency "rest-client", ">= 1.4.1"
37
+ gem.add_dependency "json_pure", ">= 1.2.2"
38
+ gem.add_dependency "activesupport", ">= 3.0.0.beta"
39
+ gem.add_development_dependency "rspec", ">= 1.2.9"
40
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
41
+ end
42
+ Jeweler::GemcutterTasks.new
43
+ Jeweler::RubyforgeTasks.new do |rubyforge|
44
+ rubyforge.doc_task = "rdoc"
45
+ end
46
+ rescue LoadError
47
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
48
+ end
49
+
50
+ require 'spec/rake/spectask'
51
+ Spec::Rake::SpecTask.new(:spec) do |spec|
52
+ spec.libs << 'lib' << 'spec'
53
+ spec.spec_files = FileList['spec/**/*_spec.rb']
54
+ end
55
+
56
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
57
+ spec.libs << 'lib' << 'spec'
58
+ spec.pattern = 'spec/**/*_spec.rb'
59
+ spec.rcov = true
60
+ end
61
+
62
+ task :spec => :check_dependencies
63
+
64
+ task :default => :spec
65
+
66
+ require 'rake/rdoctask'
67
+ Rake::RDocTask.new do |rdoc|
68
+ rdoc.rdoc_dir = 'rdoc'
69
+ rdoc.title = "rid #{Rid::VERSION}"
70
+ rdoc.rdoc_files.include('README*')
71
+ rdoc.rdoc_files.include('lib/**/*.rb')
72
+ end
data/bin/rid ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby1.8
2
+
3
+ rid_path = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(rid_path) if File.directory?(rid_path) && !$:.include?(rid_path)
5
+
6
+ require 'rid'
7
+ require 'rid/commands'
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'json'
4
+
5
+ module Rid
6
+ CONFIG_FILENAME = ".ridrc"
7
+
8
+ def self.root
9
+ @root ||= find_root
10
+ end
11
+
12
+ def self.database
13
+ @database ||= config["database"]
14
+ end
15
+
16
+ def self.id
17
+ @id ||= File.read(File.join(root, '_id')).strip
18
+ end
19
+
20
+ def self.rev
21
+ @rev ||= File.read(File.join(root, '_rev')).strip rescue nil
22
+ end
23
+
24
+ private
25
+
26
+ def self.config
27
+ @config ||= YAML.load(File.open config_file)
28
+ end
29
+
30
+ def self.config_file
31
+ File.join root, CONFIG_FILENAME
32
+ end
33
+
34
+ def self.find_root
35
+ cwd = Dir.pwd
36
+ return cwd if File.exists?(File.join(cwd, CONFIG_FILENAME))
37
+ Dir.chdir("..") do
38
+ find_root unless cwd == Dir.pwd
39
+ end
40
+ rescue SystemCallError
41
+ # could not chdir, no problem just return
42
+ end
43
+ end
@@ -0,0 +1,15 @@
1
+ require 'thor/group'
2
+ require 'active_support/inflector'
3
+
4
+ module Rid
5
+ module Actions
6
+ class Base < Thor::Group
7
+ include Thor::Actions
8
+
9
+ def self.banner
10
+ "rid #{to_s.split('::').last.underscore}"
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,28 @@
1
+ require 'rid/actions/base'
2
+ require 'rid/design_document'
3
+
4
+ require "rest_client"
5
+
6
+ module Rid
7
+ module Actions
8
+ class Pull < Base
9
+ add_runtime_options!
10
+
11
+ def pull
12
+ doc = DesignDocument.new
13
+ say "Pulling %s" % doc.url
14
+
15
+ resp = RestClient.get doc.url(:attachments => true)
16
+ doc.json = resp.body
17
+
18
+ doc.write do |filename, content|
19
+ create_file filename, content
20
+ end
21
+
22
+ say "Checked out %s" % doc.rev
23
+ rescue RestClient::ResourceNotFound
24
+ say "Error: Document %s does not exist!" % doc.id
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,53 @@
1
+ require 'rid/actions/base'
2
+ require 'rid/design_document'
3
+
4
+ require 'pathname'
5
+ require "rest_client"
6
+
7
+ module Rid
8
+ module Actions
9
+ class Push < Base
10
+ def initialize(*args)
11
+ super
12
+ @doc = DesignDocument.new
13
+ end
14
+
15
+ def create_database_unless_exists
16
+ RestClient.put @doc.database, nil
17
+ say "Created database %s" % @doc.database
18
+ rescue RestClient::PreconditionFailed
19
+ end
20
+
21
+ def push
22
+ root = Pathname.new(destination_root)
23
+ filenames = Dir[root.join "**/*"]
24
+ filenames.map! { |file| Pathname.new file }
25
+ filenames.map! { |path| path.relative_path_from root }
26
+ filenames.delete_if { |path| !path.file? }
27
+ filenames.map!(&:to_s)
28
+ @doc.read(filenames) do |filename|
29
+ File.read File.join(destination_root, filename)
30
+ end
31
+
32
+ say "Pushing to %s" % @doc.url
33
+
34
+ resp = RestClient.put @doc.url, @doc.json
35
+ response = JSON.parse(resp.body)
36
+
37
+ if response["ok"]
38
+ @doc.rev = response["rev"]
39
+ File.open File.join(destination_root, "_rev"), "w" do |file|
40
+ file << @doc.rev
41
+ end
42
+
43
+ say "Pushed %s" % @doc.rev
44
+ else
45
+ say "Error occured: %s" % response.inspect
46
+ end
47
+
48
+ rescue RestClient::Conflict
49
+ say "Conflict! Try to pull first or delete ./_rev."
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,41 @@
1
+ require 'rid/actions/base'
2
+
3
+ module Rid
4
+ module Actions
5
+ class Routes < Base
6
+ def routes
7
+ say 'Static:'
8
+ Dir.glob(File.join(destination_root, "_attachments/*.html")).each do |file|
9
+ say ' %s' % attachment_url(file)
10
+ end
11
+
12
+ say 'Lists:'
13
+ Dir.glob(File.join(destination_root, "lists/*")).each do |list|
14
+ Dir.glob(File.join(destination_root, "views/*")).each do |view|
15
+ say ' %s' % list_url(list, view)
16
+ end
17
+ end
18
+
19
+ say 'Shows:'
20
+ Dir.glob(File.join(destination_root, "shows/*")).each do |show|
21
+ say ' %s' % show_url(show)
22
+ say ' %s' % show_url(show, '/:id')
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def attachment_url(file)
29
+ File.join(Rid.database, '_design', File.basename(Rid.database), File.basename(file))
30
+ end
31
+
32
+ def list_url(list, view)
33
+ File.join(Rid.database, '_design', File.basename(Rid.database), '_list', File.basename(view), File.basename(list, '.js'))
34
+ end
35
+
36
+ def show_url(show, id = '/')
37
+ File.join(Rid.database, '_design', File.basename(Rid.database), '_show', File.basename(show, '.js'), id)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ module Rid
2
+ module Attachments
3
+ # Mime type mapping from extensions
4
+ MIME_TYPE_MAPPING = {
5
+ ".html" => "text/html",
6
+ ".js" => "text/javascript",
7
+ ".css" => "text/css",
8
+ }
9
+
10
+ def reduce_attachments!
11
+ return hash unless hash["_attachments"]
12
+ attachments = {}
13
+ hash["_attachments"].each do |key, value|
14
+ data = value["data"]
15
+ next unless data
16
+ attachments.update key => decode_attachment(data)
17
+ end
18
+ hash.update "_attachments" => attachments
19
+ end
20
+
21
+ def map_attachments!
22
+ return unless hash["_attachments"]
23
+ attachments = {}
24
+ flatten_attachements(hash["_attachments"]).each do |key, value|
25
+ attachments.update key => {
26
+ "data" => encode_attachment(value),
27
+ "content_type" => mime_type_for(key)
28
+ }
29
+ end
30
+ self.hash.update "_attachments" => attachments
31
+ end
32
+
33
+ def flatten_attachements(doc, base = nil)
34
+ result = {}
35
+ doc.each do |key, value|
36
+ new_base = base ? [base, key].join('/') : key
37
+ if value.is_a?(Hash)
38
+ result.update flatten_attachements(value, new_base)
39
+ else
40
+ result.update new_base => value
41
+ end
42
+ end
43
+ result
44
+ end
45
+
46
+ def decode_attachment(data)
47
+ data.unpack("m").first
48
+ end
49
+
50
+ def encode_attachment(data)
51
+ [data].pack("m").gsub(/\s+/,'')
52
+ end
53
+
54
+ def mime_type_for(filename)
55
+ ext = File.extname(filename)
56
+ MIME_TYPE_MAPPING[ext] || 'text/plain'
57
+ end
58
+ end
59
+ end