rid 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/.gitmodules +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +93 -0
- data/Rakefile +72 -0
- data/bin/rid +7 -0
- data/lib/rid.rb +43 -0
- data/lib/rid/actions/base.rb +15 -0
- data/lib/rid/actions/pull.rb +28 -0
- data/lib/rid/actions/push.rb +53 -0
- data/lib/rid/actions/routes.rb +41 -0
- data/lib/rid/attachments.rb +59 -0
- data/lib/rid/commands.rb +41 -0
- data/lib/rid/commands/destroy.rb +9 -0
- data/lib/rid/commands/generate.rb +9 -0
- data/lib/rid/commands/pull.rb +4 -0
- data/lib/rid/commands/push.rb +4 -0
- data/lib/rid/commands/routes.rb +4 -0
- data/lib/rid/design_document.rb +179 -0
- data/lib/rid/generators.rb +63 -0
- data/lib/rid/generators/application/USAGE +10 -0
- data/lib/rid/generators/application/application_generator.rb +51 -0
- data/lib/rid/generators/application/templates/README +1 -0
- data/lib/rid/generators/application/templates/_attachments/index.html +11 -0
- data/lib/rid/generators/application/templates/_attachments/stylesheets/application.css +25 -0
- data/lib/rid/generators/application/templates/_id +1 -0
- data/lib/rid/generators/application/templates/gitignore +0 -0
- data/lib/rid/generators/application/templates/lib/mustache.js +305 -0
- data/lib/rid/generators/application/templates/ridrc +1 -0
- data/lib/rid/generators/application/templates/validate_doc_update.js +3 -0
- data/lib/rid/generators/base.rb +66 -0
- data/lib/rid/generators/list/USAGE +8 -0
- data/lib/rid/generators/list/list_generator.rb +9 -0
- data/lib/rid/generators/list/templates/list.js +29 -0
- data/lib/rid/generators/named_base.rb +22 -0
- data/lib/rid/generators/scaffold/USAGE +10 -0
- data/lib/rid/generators/scaffold/scaffold_generator.rb +28 -0
- data/lib/rid/generators/show/USAGE +8 -0
- data/lib/rid/generators/show/show_generator.rb +9 -0
- data/lib/rid/generators/show/templates/show.js +20 -0
- data/lib/rid/generators/validation/USAGE +9 -0
- data/lib/rid/generators/validation/templates/validate_doc_update.js +3 -0
- data/lib/rid/generators/validation/validation_generator.rb +34 -0
- data/lib/rid/generators/view/USAGE +8 -0
- data/lib/rid/generators/view/templates/map.js +5 -0
- data/lib/rid/generators/view/view_generator.rb +17 -0
- data/lib/rid/makros.rb +105 -0
- data/lib/rid/version.rb +3 -0
- data/rid.gemspec +113 -0
- data/spec/rid/design_document_spec.rb +329 -0
- data/spec/rid_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +187 -0
data/.gitignore
ADDED
data/.gitmodules
ADDED
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.
|
data/README.rdoc
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
data/lib/rid.rb
ADDED
@@ -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,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
|