├── .document ├── .gitignore ├── HISTORY ├── LICENSE ├── README.rdoc ├── Rakefile ├── bin └── choco ├── choco.gemspec ├── lib ├── choco.rb └── choco │ ├── cli.rb │ ├── dependency_manager.rb │ └── generators │ ├── app_generator.rb │ ├── controller_generator.rb │ ├── fixture_generator.rb │ ├── layout_generator.rb │ ├── model_generator.rb │ ├── plugin_generator.rb │ ├── scaffold_generator.rb │ └── templates │ ├── Jimfile │ ├── README.rdoc │ ├── Rakefile │ ├── application.css │ ├── choco │ ├── config.ru │ ├── controllers │ ├── application_controller.js │ ├── base_controller.js │ └── rest_controller.js │ ├── fixtures │ ├── base.js │ └── model │ │ └── array.js │ ├── help │ ├── commands │ └── generators │ ├── helpers │ └── application_helper.js │ ├── index.html │ ├── lib │ ├── application.js │ └── plugin.js │ ├── models │ └── base.js │ └── views │ ├── edit.template │ ├── index.template │ ├── layout.js │ ├── main │ └── index.template │ ├── new.template │ └── show.template └── spec ├── choco_spec.rb ├── dependency_manager_spec.rb ├── generators_spec.rb ├── spec.opts └── spec_helper.rb /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/**/*.rb 3 | bin/* 4 | features/**/*.feature 5 | LICENSE 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 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 22 | 23 | spec/tmp/* 24 | -------------------------------------------------------------------------------- /HISTORY: -------------------------------------------------------------------------------- 1 | == 0.1.3 [08-31-10] 2 | * Add $ choco server command to launch a local rack server 3 | * A config.ru file is created at the root of each new project 4 | 5 | == 0.1.2 [08-27-10] 6 | * Add fixtures generate (mock ajax requests) 7 | 8 | == 0.1.1 [07-27-10] 9 | 10 | * Clean generated HTML (indentation) 11 | * Add lib/application.js file at project generation 12 | * Remove compressed folder 13 | * When a controller is deleted, the watcher script removes it from the app controller 14 | * Add specifications 15 | 16 | == 0.1.0 [07-23-10] 17 | 18 | * Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Anthony Heukmes 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. 21 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Choco 2 | 3 | A delicious Javascript web framework made in Belgium! 4 | 5 | Choco brings the MVC to the client side! 6 | 7 | You like Javascript and you want to develop rich internet applications? 8 | You also know that HTML & CSS are powerful? 9 | Cappuccino & Sproutcore don't feel like web development anymore? 10 | 11 | Thanks to Choco, you'll be able to easily develop maintainable web applications. 12 | A Choco app consists of only one HTML page, all the interactions are managed by Javascript. 13 | Your UI only uses HTML and CSS! 14 | 15 | Choco is based on powerful libaries : 16 | 17 | * Sammy (http://github.com/quirkey/sammy) 18 | * js-model (http://github.com/benpickles/js-model) 19 | * Jim (http://github.com/quirkey/jim) 20 | 21 | Huge thanks to Aaron Quint and Ben Pickles for their incredible work. 22 | 23 | A sample project based on Rails 3 is available here : http://github.com/ahe/choco_demo 24 | 25 | An awesome screencast with an awesome belgian accent is available here : 26 | http://www.2dconcept.com/images/choco.mov 27 | 28 | Follow us on Twitter : https://twitter.com/choco_js 29 | 30 | == Installation 31 | 32 | Choco is a Ruby gem, simply install it : 33 | 34 | $ gem install choco 35 | 36 | == A new application 37 | 38 | $ choco new my_project 39 | 40 | This will generate your project structure. 41 | 42 | You can then install the required JS dependencies (jQuery, Sammy, ...) by executing the following command at the root of your project : 43 | 44 | $ rake choco:js:install 45 | 46 | Launch your local server : 47 | 48 | $ choco server 49 | 50 | 51 | == Location 52 | 53 | A Choco app is composed of static files (js, css, images, ...), it must be directly accessible on your web server. 54 | 55 | A local Rack web server can be launched directly using the '$ choco server' command. You'll have to install WEBRick or Mongrel. 56 | 57 | If you use Rails, you can for example put your Choco app inside the public/javascripts folder. 58 | 59 | Once you've chosen the location where your application will reside, don't forget to configure the path to your view files. 60 | You can find the line to edit in your application_controller (app/controllers). 61 | 62 | Example for Rails : 63 | 64 | this.project_path = '/javascripts/my_project'; 65 | 66 | == Jim 67 | 68 | Jim is mix of Ruby gems & Bundler but for Javascript libraries. 69 | 70 | When you install a JS library using the $ jim install command, it is stored in your home folder (~/.jim/lib). 71 | All your projects can then use this repository to load the required dependencies. 72 | 73 | The $ rake choco:js:install command installs all the dependencies you need to run a Choco app. 74 | 75 | A Jimfile is located at the root of your project, it lists all these dependencies. 76 | They can be libraries (jQuery, Sammy, ...) but also local JS files of your application (controllers, models, ...). 77 | 78 | All these files are bundled into the compressed/bundled.js file, so you only have to include this file in your HTML page and all your Choco app will be loaded. 79 | 80 | You can continuously track changes in your JS files by running the following command in your project root folder : 81 | 82 | $ choco --watch 83 | 84 | The watch script will track your JS files (creation, edition, suppression) and automatically update your Jimfile & bundled.js to reflect these changes. 85 | You never have to worry about including your JS files inside your HTML page, just include bundled.js! 86 | 87 | == Access your homepage 88 | 89 | To launch your application, just call the index.html page. 90 | I'm using Rails, I will call the following URL in my web browser : 91 | http://localhost:3000/javascripts/choco_app/index.html 92 | 93 | Notice that when the app is launched, the URL changes to : 94 | http://localhost:3000/javascripts/choco_app/index.html#/main 95 | 96 | #/main is a route in your Choco app (thanks to Sammy), it is defined in your ApplicationController. 97 | 98 | this.get('#/main', function(cx) { 99 | }); 100 | 101 | You have to include this # in every HTML link you add into your views. 102 | 103 | List all the posts 104 | Add a new post 105 | ... 106 | 107 | = Project structure 108 | 109 | A Choco project has the following structure : 110 | 111 | === Jimfile 112 | This file list all the dependencies for your project (libraries & local files). 113 | 114 | === app 115 | It contains four sub-folders : controllers, helpers, models & views. 116 | This is where you will spend most of your development time. 117 | 118 | === compressed 119 | This is where the bundled.js file is created, it bundles all your JS files in one single file. 120 | It is highly recommended to compress this file before going live (rake choco:deploy). 121 | 122 | === images 123 | Store all the images used by your application in this folder. 124 | 125 | === index.html 126 | This is the file you have to open in your browser to launch your Choco application. 127 | It includes your bundled.js file, your stylesheets and defines the layout of your application. 128 | 129 | The #choco div is very important, this is where the HTML generated by your views will be inserted. 130 | If you change his name, you must configure it into the app/controllers/application_controller.js as well. 131 | 132 | === lib 133 | Use this folder to store your JS files which are specific to your project but don't have their place in the app folder. 134 | If this is a library that can be used by other projects, you should use Jim instead. 135 | 136 | === script 137 | This folder contains the choco script file. 138 | It brings you a few generators (model, controller, layout, scaffold, json, plugin). 139 | 140 | === spec 141 | Test your application using Behavior Driven Development (BDD). 142 | 143 | === stylesheets 144 | Store all the stylesheets used by your application in this folder. 145 | 146 | = A first resource (CRUD) 147 | 148 | First of all, don't forget to start the choco watcher ($ choco --watch) if you don't want to update your Jimfile manually. 149 | 150 | The easiest way to get started is to generate a scaffold based on JSON. 151 | Use your server side technology to create a web service that returns JSON representing your resource. 152 | 153 | For example, with Rails : 154 | 155 | def index 156 | render :json => Post.all 157 | end 158 | 159 | Once you have that, you can generate your scaffold very easily : 160 | 161 | $ choco generate scaffold post http://localhost:3000/posts 162 | 163 | This will create the PostsController with all the actions, the views and the model. 164 | 165 | Notice that the Choco watcher automatically updated your Jimfile and your bundled.js. 166 | 167 | It also updated your ApplicationController by adding the following line : 168 | 169 | PostsController(this); 170 | 171 | You can now call this new page with the following URL : 172 | http://localhost:3000/javascripts/choco_app/index.html#/posts 173 | 174 | This will send a GET request to the #/posts URL of your application. 175 | This request will be handled by your PostsController and more precisely by this action : 176 | 177 | get('#/posts', function(cx) { 178 | cx.posts = Post.all(); 179 | }); 180 | 181 | We are using our model to get all the posts (locally) and store them in the posts variable. 182 | Using cx will make this variable available in your view. 183 | 184 | Like Rails, Choco will automatically render the view with the same name as the current action (located in the view folder with the same name as the current controller). 185 | In our case app/views/posts/index.template will be rendered. 186 | 187 | This is a simple HTML page with some Javascript tags. 188 | 189 | Here is a part of it : 190 | 191 | <% $.each(posts, function(i, post) { %> 192 | 193 | 194 | <%= post.attr('created_at') %> 195 | 196 | 197 | <%= post.attr('author') %> 198 | 199 | ... 200 | 201 | We are iterating on our posts array to create a table row for each of them. 202 | 203 | You will not see any post yet because you haven't loaded the post from your server (JSON). 204 | 205 | To do that, just update your ApplicationController by adding your Post model in the models array : 206 | 207 | var models = [Post]; 208 | ChocoUtils.loadModels(models, function() { 209 | app.run('#/main'); 210 | }); 211 | 212 | All your models will be loaded by calling the load() method on their class. 213 | This action will send Ajax requests to your server. 214 | The Choco application will then be launched when all the responses have been received and treated. 215 | 216 | To create, remove & update posts, you have to create your REST controller on the server side. 217 | Have a look at the sample demo for an example (http://github.com/ahe/choco_demo). 218 | 219 | You can also use fixtures instead. Fixtures are located in the /fixtures folder, they use the jquery.mockjax plugin to mock Ajax requests and return the JSON you defined in your fixtures. 220 | 221 | Use the choco generate fixture command to easily create your fixtures (e.g : $ choco generate fixture post, will mock requests sent to the /posts URL). 222 | 223 | = Generators 224 | 225 | The following code generators are available : 226 | 227 | * $ choco generate controller 228 | * $ choco generate model 229 | * $ choco generate scaffold [field1 field2 ...] 230 | * $ choco generate scaffold 231 | * $ choco generate fixture 232 | * $ choco generate layout 233 | * $ choco generate plugin 234 | 235 | Don't forget to start the choco --watch command before executing any generator. Otherwise you'll have to update manually your Jimfile, bundled.js & application_controller.js. 236 | 237 | = Controllers 238 | 239 | To learn more about controllers, please read Sammy documentation : http://code.quirkey.com/sammy 240 | 241 | You may be wondering about the best solution to execute Javascript code after rendering a template. 242 | Let's say we want to add a specific behavior to one of our link, in the app/views/posts/index.template view, I add this simple link : 243 | 244 | Hey, click me, I'm a simple test! 245 | 246 | I want to open a Javascript alert when it is clicked. 247 | 248 | I can simply update my controller like this : 249 | 250 | get('#/posts', function(cx) { 251 | cx.posts = Post.all(); 252 | cx.render({ template: 'posts/index', event: 'posts_loaded', data: { size: cx.posts.length }}); 253 | }); 254 | 255 | bind('posts_loaded', function(e, data) { 256 | $('#test_link').click(function() { 257 | alert('Hey! We have ' + data['size'] + ' posts!'); 258 | return false; 259 | }); 260 | }); 261 | 262 | I call the render() method explicitly because I want to trigger an event when the template is rendered. 263 | This event is named 'posts_loaded' and when it is executed it simply add a Javascript behavior to my link. 264 | 265 | The render method supports various options like like layouts, events & more. 266 | You can read the doc here : http://github.com/ahe/choco.libs/blob/master/plugins/sammy/choco.plugins.sammy.smart_renderer.js 267 | 268 | = Views 269 | 270 | By default, Choco uses simple template views where you can combine HTML and Javascript. 271 | 272 | You can easily display values in your views : 273 | 274 | <%= post.attr('title') %> 275 | 276 | Or call helpers you defined : 277 | 278 | <% myHelper(); %> // Don't forget the ; 279 | 280 | You can also of course use conditions, iterators & more. 281 | 282 | You can easily switch to HAML or Mustache by configuring it in your application controller. 283 | Have a look at Sammy documentation for more information. 284 | 285 | To create a DELETE link, just use the following attributes : 286 | 287 | Delete 288 | 289 | = Models 290 | 291 | For more information about models, please read js-model documentation : http://github.com/benpickles/js-model 292 | 293 | js-model stores all the data locally. You can persist your models to the server using methods like save() but don't forget to implement your REST controller on the server side. 294 | Have a look at the demo for an example : http://github.com/ahe/choco_demo 295 | 296 | js-model expects JSON without the ROOT element included. In Rails that means you have to disable this option : 297 | 298 | ActiveRecord::Base.include_root_in_json = false 299 | 300 | = Logger 301 | 302 | You can call the logger from anywhere in your application by simply calling : 303 | 304 | app.logger('hello choco!'); 305 | 306 | = Plugins 307 | 308 | Models, views & controllers can easily be extended using plugins. 309 | 310 | You can generate a new plugin using the following command : 311 | 312 | $ choco generate plugin 313 | 314 | Existing plugins & Choco libs are stored here : http://github.com/ahe/choco.libs 315 | 316 | = Deploy 317 | 318 | When you deploy your application on live, don't forget to compress your files : 319 | 320 | $ rake choco:deploy 321 | 322 | Then just copy the required files on your web server. 323 | 324 | = BDD 325 | 326 | BDD is currently being implemented, it will be soon very easy to test all the parts of your Choco apps. 327 | A first solution can be found here : http://github.com/ahe/sammy_demo/tree/master/test/javascript 328 | 329 | = Mailing list 330 | 331 | http://groups.google.com/group/choco-js 332 | 333 | = Note on Patches/Pull Requests 334 | 335 | * Fork the project. 336 | * Make your feature addition or bug fix. 337 | * Add tests for it. This is important so I don't break it in a 338 | future version unintentionally. 339 | * Commit, do not mess with rakefile, version, or history. 340 | (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) 341 | * Send me a pull request. Bonus points for topic branches. 342 | 343 | = Copyright 344 | 345 | Copyright (c) 2010 Anthony Heukmes. See LICENSE for details. 346 | 347 | * http://2dconcept.com 348 | * http://intotheweb.be 349 | * http://twitter.com/2dc 350 | * http://twitter.com/intotheweb 351 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) 5 | 6 | require 'choco' 7 | 8 | begin 9 | require 'jeweler' 10 | Jeweler::Tasks.new do |gem| 11 | gem.name = "choco" 12 | gem.version = Choco::VERSION 13 | gem.summary = %Q{A delicious Javascript web framework made in Belgium!} 14 | gem.description = %Q{Choco brings the MVC to the client side! It allows you to easily develop maintainable Rich Internet Applications using Javascript.} 15 | gem.email = "anthony.heukmes@skynet.be" 16 | gem.homepage = "http://github.com/ahe/choco" 17 | gem.authors = ["Anthony Heukmes"] 18 | 19 | gem.add_dependency "jim" 20 | gem.add_dependency "fssm" 21 | gem.add_dependency "thor" 22 | gem.add_dependency "activesupport" 23 | gem.add_dependency "rack" 24 | 25 | gem.add_development_dependency "rspec", ">= 1.2.9" 26 | end 27 | Jeweler::GemcutterTasks.new 28 | rescue LoadError 29 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 30 | end 31 | 32 | require 'spec/rake/spectask' 33 | Spec::Rake::SpecTask.new(:spec) do |spec| 34 | spec.libs << 'lib' << 'spec' 35 | spec.spec_files = FileList['spec/**/*_spec.rb'] 36 | end 37 | 38 | Spec::Rake::SpecTask.new(:rcov) do |spec| 39 | spec.libs << 'lib' << 'spec' 40 | spec.pattern = 'spec/**/*_spec.rb' 41 | spec.rcov = true 42 | end 43 | 44 | task :spec => :check_dependencies 45 | 46 | task :default => :spec 47 | 48 | require 'rake/rdoctask' 49 | Rake::RDocTask.new do |rdoc| 50 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 51 | 52 | rdoc.rdoc_dir = 'rdoc' 53 | rdoc.title = "choco #{version}" 54 | rdoc.rdoc_files.include('README*') 55 | rdoc.rdoc_files.include('lib/**/*.rb') 56 | end 57 | -------------------------------------------------------------------------------- /bin/choco: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'choco' 4 | require 'pathname' 5 | 6 | module Choco 7 | module ScriptChocoLoader 8 | SCRIPT_CHOCO = File.join('script', 'choco') 9 | 10 | def self.exec_script_choco! 11 | cwd = Dir.pwd 12 | exec 'ruby', SCRIPT_CHOCO, *ARGV if in_choco_application? 13 | 14 | Dir.chdir("..") do 15 | # Recurse in a chdir block: if the search fails we want to be sure 16 | # the application is generated in the original working directory. 17 | exec_script_choco! unless cwd == Dir.pwd 18 | end 19 | rescue SystemCallError 20 | # could not chdir, no problem just return 21 | end 22 | 23 | def self.in_choco_application? 24 | File.exists?(SCRIPT_CHOCO) || in_choco_application_subdirectory? 25 | end 26 | 27 | def self.in_choco_application_subdirectory?(path = Pathname.new(Dir.pwd)) 28 | File.exists?(File.join(path, SCRIPT_CHOCO)) || !path.root? && in_choco_application_subdirectory?(path.parent) 29 | end 30 | end 31 | end 32 | 33 | Choco::ScriptChocoLoader.exec_script_choco! 34 | 35 | ARGV << '--help' if ARGV.empty? 36 | command = ARGV.shift 37 | 38 | case command 39 | when 'new' 40 | Choco::AppGenerator.start ARGV 41 | when '-v' 42 | puts "Choco version #{Choco::VERSION}" 43 | else 44 | puts "\nWelcome to Choco!\nA delicious Javascript web framework made in Belgium!\n\nUsage:\n\n$ choco new \n-> Generates a new Choco project\n\n$ choco -v\n-> Displays current version\n" 45 | end -------------------------------------------------------------------------------- /choco.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{choco} 8 | s.version = "0.1.3" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Anthony Heukmes"] 12 | s.date = %q{2010-08-09} 13 | s.default_executable = %q{choco} 14 | s.description = %q{Choco brings the MVC to the client side! It allows you to easily develop maintainable Rich Internet Applications using Javascript.} 15 | s.email = %q{anthony.heukmes@skynet.be} 16 | s.executables = ["choco"] 17 | s.extra_rdoc_files = [ 18 | "LICENSE", 19 | "README.rdoc" 20 | ] 21 | s.files = [ 22 | ".document", 23 | ".gitignore", 24 | "HISTORY", 25 | "LICENSE", 26 | "README.rdoc", 27 | "Rakefile", 28 | "bin/choco", 29 | "choco.gemspec", 30 | "lib/choco.rb", 31 | "lib/choco/cli.rb", 32 | "lib/choco/dependency_manager.rb", 33 | "lib/choco/generators/app_generator.rb", 34 | "lib/choco/generators/controller_generator.rb", 35 | "lib/choco/generators/fixture_generator.rb", 36 | "lib/choco/generators/layout_generator.rb", 37 | "lib/choco/generators/model_generator.rb", 38 | "lib/choco/generators/plugin_generator.rb", 39 | "lib/choco/generators/scaffold_generator.rb", 40 | "lib/choco/generators/templates/Jimfile", 41 | "lib/choco/generators/templates/README.rdoc", 42 | "lib/choco/generators/templates/Rakefile", 43 | "lib/choco/generators/templates/application.css", 44 | "lib/choco/generators/templates/choco", 45 | "lib/choco/generators/templates/config.ru", 46 | "lib/choco/generators/templates/controllers/application_controller.js", 47 | "lib/choco/generators/templates/controllers/base_controller.js", 48 | "lib/choco/generators/templates/controllers/rest_controller.js", 49 | "lib/choco/generators/templates/fixtures/base.js", 50 | "lib/choco/generators/templates/fixtures/model/array.js", 51 | "lib/choco/generators/templates/help/commands", 52 | "lib/choco/generators/templates/help/generators", 53 | "lib/choco/generators/templates/helpers/application_helper.js", 54 | "lib/choco/generators/templates/index.html", 55 | "lib/choco/generators/templates/lib/application.js", 56 | "lib/choco/generators/templates/lib/plugin.js", 57 | "lib/choco/generators/templates/models/base.js", 58 | "lib/choco/generators/templates/views/edit.template", 59 | "lib/choco/generators/templates/views/index.template", 60 | "lib/choco/generators/templates/views/layout.js", 61 | "lib/choco/generators/templates/views/main/index.template", 62 | "lib/choco/generators/templates/views/new.template", 63 | "lib/choco/generators/templates/views/show.template", 64 | "spec/choco_spec.rb", 65 | "spec/dependency_manager_spec.rb", 66 | "spec/generators_spec.rb", 67 | "spec/spec.opts", 68 | "spec/spec_helper.rb" 69 | ] 70 | s.homepage = %q{http://github.com/ahe/choco} 71 | s.rdoc_options = ["--charset=UTF-8"] 72 | s.require_paths = ["lib"] 73 | s.rubygems_version = %q{1.3.6} 74 | s.summary = %q{A delicious Javascript web framework made in Belgium!} 75 | s.test_files = [ 76 | "spec/choco_spec.rb", 77 | "spec/dependency_manager_spec.rb", 78 | "spec/generators_spec.rb", 79 | "spec/spec_helper.rb" 80 | ] 81 | 82 | if s.respond_to? :specification_version then 83 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 84 | s.specification_version = 3 85 | 86 | if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then 87 | s.add_runtime_dependency(%q, [">= 0"]) 88 | s.add_runtime_dependency(%q, [">= 0"]) 89 | s.add_runtime_dependency(%q, [">= 0"]) 90 | s.add_runtime_dependency(%q, [">= 0"]) 91 | s.add_runtime_dependency(%q, [">= 0"]) 92 | s.add_development_dependency(%q, [">= 1.2.9"]) 93 | else 94 | s.add_dependency(%q, [">= 0"]) 95 | s.add_dependency(%q, [">= 0"]) 96 | s.add_dependency(%q, [">= 0"]) 97 | s.add_dependency(%q, [">= 0"]) 98 | s.add_dependency(%q, [">= 0"]) 99 | s.add_dependency(%q, [">= 1.2.9"]) 100 | end 101 | else 102 | s.add_dependency(%q, [">= 0"]) 103 | s.add_dependency(%q, [">= 0"]) 104 | s.add_dependency(%q, [">= 0"]) 105 | s.add_dependency(%q, [">= 0"]) 106 | s.add_dependency(%q, [">= 0"]) 107 | s.add_dependency(%q, [">= 1.2.9"]) 108 | end 109 | end 110 | 111 | -------------------------------------------------------------------------------- /lib/choco.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'thor/group' 3 | require 'active_support/inflector' 4 | 5 | module Choco 6 | VERSION = '0.1.3' 7 | 8 | autoload :DependencyManager, 'choco/dependency_manager' 9 | autoload :CLI, 'choco/cli' 10 | autoload :AppGenerator, 'choco/generators/app_generator' 11 | autoload :ControllerGenerator, 'choco/generators/controller_generator' 12 | autoload :LayoutGenerator, 'choco/generators/layout_generator' 13 | autoload :FixtureGenerator, 'choco/generators/fixture_generator' 14 | autoload :ModelGenerator, 'choco/generators/model_generator' 15 | autoload :PluginGenerator, 'choco/generators/plugin_generator' 16 | autoload :ScaffoldGenerator, 'choco/generators/scaffold_generator' 17 | end -------------------------------------------------------------------------------- /lib/choco/cli.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | module Choco 4 | 5 | class CLI 6 | 7 | def initialize(args) 8 | @args = args 9 | @output = "" 10 | end 11 | 12 | def run 13 | @args << '--help' if @args.empty? 14 | 15 | aliases = { 16 | "g" => "generate", 17 | "s" => "server" 18 | } 19 | 20 | command = @args.shift 21 | command = aliases[command] || command 22 | 23 | case command 24 | when 'generate' 25 | name = @args.shift 26 | if name 27 | klass = eval("Choco::#{name.capitalize}Generator") 28 | klass.start @args 29 | else 30 | @output = template('generators') 31 | end 32 | 33 | when 'server' 34 | port = 9292 35 | port = @args[@args.index('-p') + 1] if @args.include?('-p') 36 | port = @args[@args.index('--port') + 1] if @args.include?('--port') 37 | puts "*** Choco server is now running on port #{port}" 38 | puts "*** Launch http://localhost:#{port}/index.html to start your Choco application" 39 | system "rackup #{@args.join(' ')}" 40 | 41 | when '--watch' 42 | require 'fssm' 43 | 44 | puts "*** Choco is now watching your JS files..." 45 | 46 | system "jim bundle #{Choco::DependencyManager.to}" 47 | 48 | FSSM.monitor(Dir.pwd, ['**/*.js', 'Jimfile']) do 49 | update do |base, relative| 50 | unless relative.include?('bundled.js') 51 | puts "*** #{relative} changed!" 52 | system "jim bundle #{Choco::DependencyManager.to}" 53 | end 54 | end 55 | 56 | create do |base, relative| 57 | unless relative.include?('bundled.js') 58 | puts "*** #{relative} created!" 59 | Choco::DependencyManager.add_dependency(relative) 60 | system "jim bundle #{Choco::DependencyManager.to}" 61 | end 62 | end 63 | 64 | delete do |base, relative| 65 | puts "*** #{relative} deleted!" 66 | Choco::DependencyManager.remove_dependency(relative) 67 | system "jim bundle #{Choco::DependencyManager.to}" 68 | end 69 | end 70 | else 71 | @output = template('commands') 72 | end 73 | 74 | @output 75 | end 76 | 77 | private 78 | 79 | def template(path) 80 | (Pathname.new(__FILE__).dirname + 'generators/templates/help/' + path).read 81 | end 82 | 83 | end 84 | end -------------------------------------------------------------------------------- /lib/choco/dependency_manager.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class DependencyManager 3 | 4 | @@to = 'lib/bundled.js' 5 | 6 | def self.to 7 | @@to 8 | end 9 | 10 | def self.add_dependency(filename) 11 | filename = remove_extension(filename) 12 | type = get_file_type(filename) 13 | 14 | already_present = false 15 | File.new('Jimfile', "r").each do |line| 16 | if line.include?(filename) 17 | already_present = true 18 | break 19 | end 20 | end 21 | 22 | unless already_present 23 | jimfile = "" 24 | File.new('Jimfile', "r").each do |line| 25 | jimfile << line 26 | if line.include?("// #{type}") 27 | jimfile << "#{filename}\n" 28 | end 29 | end 30 | 31 | File.open('Jimfile', "wb") { |io| io.print jimfile } 32 | 33 | if type == 'Controller' || type == 'Helper' 34 | application_controller = "" 35 | file = extract_filename(filename) 36 | 37 | File.new('app/controllers/application_controller.js', "r").each do |line| 38 | application_controller << line 39 | if line.include?("/* Configure your #{type.downcase.pluralize} here") 40 | if type == 'Helper' 41 | application_controller << "\tthis.helpers(#{file});\n" 42 | else 43 | application_controller << "\t#{file}(this);\n" 44 | end 45 | end 46 | end 47 | 48 | File.open('app/controllers/application_controller.js', "wb") { |io| io.print application_controller } 49 | end 50 | end 51 | end 52 | 53 | def self.remove_dependency(filename) 54 | filename = remove_extension(filename) 55 | type = get_file_type(filename) 56 | 57 | jimfile = "" 58 | File.new('Jimfile', "r").each do |line| 59 | jimfile << line unless line.include?(filename) 60 | end 61 | 62 | File.open('Jimfile', "wb") { |io| io.print jimfile } 63 | 64 | file = extract_filename(filename) 65 | application_controller = "" 66 | File.new('app/controllers/application_controller.js', "r").each do |line| 67 | if type == 'Model' 68 | application_controller << line 69 | elsif !line.include?(file) 70 | application_controller << line 71 | end 72 | end 73 | 74 | File.open('app/controllers/application_controller.js', "wb") { |io| io.print application_controller } 75 | end 76 | 77 | private 78 | 79 | def self.remove_extension(filename) 80 | filename.gsub('.js', '') 81 | end 82 | 83 | def self.extract_filename(filename) 84 | filename.to_s.split('/').last.camelcase 85 | end 86 | 87 | def self.get_file_type(filename) 88 | if filename.include?('controllers/') 89 | 'Controller' 90 | elsif filename.include?('helpers/') 91 | 'Helper' 92 | elsif filename.include?('models/') 93 | 'Model' 94 | elsif filename.include?('fixtures/') 95 | 'Fixture' 96 | else 97 | 'Libs' 98 | end 99 | end 100 | end 101 | end -------------------------------------------------------------------------------- /lib/choco/generators/app_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class AppGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates a delicious Choco app' 10 | argument :name 11 | 12 | def create_root_folder 13 | empty_directory name 14 | end 15 | 16 | def create_app_folder 17 | empty_directory "#{name}/app" 18 | empty_directory "#{name}/app/controllers" 19 | empty_directory "#{name}/app/models" 20 | empty_directory "#{name}/app/views" 21 | empty_directory "#{name}/app/views/main" 22 | empty_directory "#{name}/app/views/layouts" 23 | empty_directory "#{name}/app/helpers" 24 | 25 | template('templates/controllers/application_controller.js', "#{name}/app/controllers/application_controller.js") 26 | template('templates/views/main/index.template', "#{name}/app/views/main/index.template") 27 | template('templates/helpers/application_helper.js', "#{name}/app/helpers/application_helper.js") 28 | end 29 | 30 | def create_lib_folder 31 | empty_directory "#{name}/lib" 32 | template("templates/lib/application.js", "#{name}/lib/application.js") 33 | end 34 | 35 | def create_images_folder 36 | empty_directory "#{name}/images" 37 | end 38 | 39 | def create_stylesheets_folder 40 | empty_directory "#{name}/stylesheets" 41 | template('templates/application.css', "#{name}/stylesheets/application.css") 42 | end 43 | 44 | def create_fixtures_folder 45 | empty_directory "#{name}/fixtures" 46 | end 47 | 48 | def create_spec_folder 49 | empty_directory "#{name}/spec" 50 | end 51 | 52 | def create_script_folder 53 | empty_directory "#{name}/script" 54 | template('templates/choco', "#{name}/script/choco") 55 | end 56 | 57 | def create_jimfile 58 | template('templates/Jimfile', "#{name}/Jimfile") 59 | end 60 | 61 | def create_rakefile 62 | template('templates/Rakefile', "#{name}/Rakefile") 63 | end 64 | 65 | def create_rackup_file 66 | template('templates/config.ru', "#{name}/config.ru") 67 | end 68 | 69 | def create_readme_file 70 | create_file "#{name}/README.rdoc" 71 | end 72 | 73 | def create_index_file 74 | template('templates/index.html', "#{name}/index.html") 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/choco/generators/controller_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class ControllerGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates a Choco controller' 10 | argument :name 11 | 12 | def create_controller_file 13 | template('templates/controllers/base_controller.js', "app/controllers/#{name.underscore}_controller.js") 14 | end 15 | 16 | def create_views_folder 17 | empty_directory "app/views/#{name.underscore}" 18 | end 19 | 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/choco/generators/fixture_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class FixtureGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates Ajax fixtures (mocks)' 10 | argument :name, :required => true 11 | 12 | def create_mock_file 13 | @name = name.underscore.pluralize 14 | template('templates/fixtures/base.js', "fixtures/#{@name}.js") 15 | end 16 | 17 | def create_json_folder 18 | empty_directory "fixtures/#{@name}" 19 | template('templates/fixtures/model/array.js', "fixtures/#{@name}/#{@name}.json") 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/choco/generators/layout_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class LayoutGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates a Choco layout' 10 | argument :name 11 | 12 | def create_model_file 13 | @layout_name = name.camelcase.singularize 14 | @name = name.underscore.singularize 15 | template('templates/views/layout.js', "app/views/layouts/#{@name}.js") 16 | end 17 | 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/choco/generators/model_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class ModelGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates a Choco model' 10 | argument :name 11 | 12 | def create_model_file 13 | @model_name = name.camelcase.singularize 14 | @model = name.underscore.singularize 15 | @route_path = name.underscore.pluralize 16 | template('templates/models/base.js', "app/models/#{@model}.js") 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/choco/generators/plugin_generator.rb: -------------------------------------------------------------------------------- 1 | module Choco 2 | class PluginGenerator < Thor::Group 3 | include Thor::Actions 4 | 5 | def self.source_root 6 | File.dirname(__FILE__) 7 | end 8 | 9 | desc 'Generates a Choco plugin (Sammy or js-model)' 10 | argument :name 11 | class_option :for_lib 12 | 13 | def create_model_file 14 | @plugin_name = name.camelcase.singularize 15 | @name = name.underscore.singularize 16 | template('templates/lib/plugin.js', "lib/plugin_#{@name}.js") 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/choco/generators/scaffold_generator.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'json' 3 | 4 | module Choco 5 | class ScaffoldGenerator < Thor::Group 6 | include Thor::Actions 7 | 8 | attr_accessor :keys 9 | 10 | def self.source_root 11 | File.dirname(__FILE__) 12 | end 13 | 14 | desc 'Generates a Choco CRUD controller with views & model' 15 | argument :name, :required => true 16 | argument :fields, :type => :array, :default => [] 17 | 18 | def get_data_structure 19 | @controller_name = name.pluralize.camelcase 20 | @route_path = name.pluralize.underscore 21 | @model = name.singularize.underscore 22 | @model_name = name.singularize.camelcase 23 | 24 | @keys = [] 25 | if fields.first && fields.first.include?('http://') 26 | @keys = load_json(fields.first) 27 | else 28 | @keys = fields 29 | end 30 | end 31 | 32 | def create_controller 33 | template('templates/controllers/rest_controller.js', "app/controllers/#{@route_path}_controller.js") 34 | end 35 | 36 | def create_views 37 | empty_directory "app/views/#{@route_path}" 38 | template('templates/views/index.template', "app/views/#{@route_path}/index.template") 39 | template('templates/views/show.template', "app/views/#{@route_path}/show.template") 40 | template('templates/views/new.template', "app/views/#{@route_path}/new.template") 41 | template('templates/views/edit.template', "app/views/#{@route_path}/edit.template") 42 | end 43 | 44 | def create_model 45 | template('templates/models/base.js', "app/models/#{@model}.js") 46 | end 47 | 48 | private 49 | 50 | def load_json(url) 51 | begin 52 | buffer = open(url, 'UserAgent' => 'Choco').read 53 | data = JSON.parse(buffer)[0] 54 | 55 | if data.size == 1 && data.first[1].is_a?(Hash) 56 | data = data.first[1] 57 | end 58 | 59 | keys = data.map{ |key, value| key } 60 | rescue Exception 61 | puts "An error occured (check that your URL is reachable and that your JSON is not empty)" 62 | end 63 | keys 64 | end 65 | 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/Jimfile: -------------------------------------------------------------------------------- 1 | // Your Jimfile lists your JS dependencies 2 | // Launch $ choco --watch at the root of your project 3 | // This file and lib/bundled.js will be maintained for you! 4 | 5 | jquery 6 | sammy 7 | sammy.template 8 | sammy.nested_params 9 | sammy.smart_renderer 10 | js-model 0.8.3 11 | underscore 12 | mustache 13 | jquery.mockjax 14 | choco.utils 15 | choco.plugins.models.load 16 | 17 | // Libs # Don't remove this line! 18 | lib/application 19 | 20 | // Helpers # Don't remove this line! 21 | app/helpers/application_helper 22 | 23 | // Models # Don't remove this line! 24 | 25 | // Controllers # Don't remove this line! 26 | app/controllers/application_controller 27 | 28 | // Fixtures # Don't remove this line! 29 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/README.rdoc: -------------------------------------------------------------------------------- 1 | == Welcome to Choco 2 | 3 | A delicious Javascript web framework made in Belgium! -------------------------------------------------------------------------------- /lib/choco/generators/templates/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | 3 | namespace :choco do 4 | 5 | namespace :js do 6 | 7 | desc "Install Javascript dependencies" 8 | task :install do 9 | system 'jim install http://code.jquery.com/jquery-1.4.2.js' 10 | system 'jim install http://github.com/quirkey/sammy/zipball/v0.5.4' 11 | system 'jim install http://github.com/benpickles/js-model/zipball/v0.8.4' 12 | system 'jim install http://documentcloud.github.com/underscore/underscore.js' 13 | system 'jim install http://github.com/janl/mustache.js/zipball/0.2.3' 14 | system 'jim install http://github.com/ahe/choco.libs/raw/master/choco.utils.js' 15 | system 'jim install http://github.com/ahe/choco.libs/raw/master/plugins/models/choco.plugins.models.load.js' 16 | system 'jim install http://github.com/ahe/choco.libs/raw/master/plugins/sammy/choco.plugins.sammy.smart_renderer.js' 17 | system 'jim install http://github.com/appendto/jquery-mockjax/raw/master/jquery.mockjax.js' 18 | system 'jim bundle lib/bundled.js' 19 | end 20 | end 21 | 22 | desc "Compress all your JS files into lib/compressed.js" 23 | task :deploy do 24 | system 'jim compress lib/compressed.js' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/application.css: -------------------------------------------------------------------------------- 1 | @import url(http://yui.yahooapis.com/3.0.0/build/cssreset/reset-min.css); -------------------------------------------------------------------------------- /lib/choco/generators/templates/choco: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' 4 | require 'choco' 5 | 6 | cli = Choco::CLI.new(ARGV) 7 | puts cli.run -------------------------------------------------------------------------------- /lib/choco/generators/templates/config.ru: -------------------------------------------------------------------------------- 1 | map '/' do 2 | run Rack::Directory.new('.') 3 | end -------------------------------------------------------------------------------- /lib/choco/generators/templates/controllers/application_controller.js: -------------------------------------------------------------------------------- 1 | var app = $.sammy(function() { 2 | 3 | this.element_selector = '#choco'; 4 | this.template_engine = 'template'; 5 | this.project_path = ''; 6 | this.fixtures = true; 7 | 8 | // Configure your Sammy JS plugins 9 | this.use(Sammy.Template); 10 | this.use(Sammy.NestedParams); 11 | this.use(Sammy.SmartRenderer, this.project_path + '/app/views/'); 12 | 13 | // Event fired by the render() method 14 | this.bind('template_loaded', function(e, data) { 15 | ChocoUtils.activateDeleteLinks(app); 16 | }); 17 | 18 | // Root page 19 | this.get('#/main', function(cx) { 20 | }); 21 | 22 | /* Configure your helpers here */ 23 | this.helpers(ApplicationHelper); 24 | 25 | /* Configure your controllers here ### Don't remove this line! */ 26 | // PostsController(this); 27 | }); 28 | 29 | $(function() { 30 | // Load your models here, example : var models = [Post, Category] 31 | var models = []; 32 | ChocoUtils.loadModels(models, function() { 33 | app.run('#/main'); 34 | }); 35 | }); -------------------------------------------------------------------------------- /lib/choco/generators/templates/controllers/base_controller.js: -------------------------------------------------------------------------------- 1 | <%= name.camelcase %>Controller = function(app) { with(app) { 2 | 3 | get('#/<%= name.underscore %>', function(cx) { 4 | }); 5 | 6 | }}; -------------------------------------------------------------------------------- /lib/choco/generators/templates/controllers/rest_controller.js: -------------------------------------------------------------------------------- 1 | <%= @controller_name %>Controller = function(app) { with(app) { 2 | 3 | /** Routes **/ 4 | 5 | // GET index 6 | get('#/<%= @route_path %>', function(cx) { 7 | cx.<%= @model.pluralize %> = <%= @model_name %>.all(); 8 | }); 9 | 10 | // GET new 11 | get('#/<%= @route_path %>/new', function(cx) { 12 | }); 13 | 14 | // POST create 15 | post('#/<%= @route_path %>', function(cx) { 16 | var <%= @model %> = new <%= @model_name %>(cx.params['<%= @model %>']); 17 | <%= @model %>.save(function(success) { 18 | cx.redirect('#/<%= @route_path %>/' + <%= @model %>.id()); 19 | }); 20 | }); 21 | 22 | // GET edit 23 | get('#/<%= @route_path %>/edit/:id', function(cx) { 24 | cx.<%= @model %> = <%= @model_name %>.find(cx.params['id']); 25 | }); 26 | 27 | // PUT update 28 | put('#/<%= @route_path %>/update/:id', function(cx) { 29 | var <%= @model %> = <%= @model_name %>.find(cx.params['id']); 30 | <%= @model %>.update(cx.params['<%= @model %>']).save(function(success) { 31 | cx.redirect('#/<%= @route_path %>/' + <%= @model %>.id()) 32 | }); 33 | }); 34 | 35 | // DELETE destroy 36 | route('delete', '#/<%= @route_path %>/:id', function(cx) { 37 | var <%= @model %> = <%= @model_name %>.find(cx.params['id']); 38 | <%= @model %>.destroy(); 39 | cx.trigger('<%= @model %>_remove', { <%= @model %>_id: <%= @model %>.id() }); 40 | }); 41 | 42 | // GET show 43 | get('#/<%= @route_path %>/:id', function(cx) { 44 | cx.<%= @model %> = <%= @model_name %>.find(cx.params['id']); 45 | }); 46 | 47 | /** Events **/ 48 | 49 | bind('<%= @model %>_remove', function(e, data) { 50 | $('#<%= @model %>_' + data['<%= @model %>_id']).remove(); 51 | }); 52 | 53 | }}; -------------------------------------------------------------------------------- /lib/choco/generators/templates/fixtures/base.js: -------------------------------------------------------------------------------- 1 | if(app.fixtures) { 2 | var model = '{ "id": 1, "title": "Hello Choco!" }'; 3 | 4 | $.mockjax({ 5 | url: '/<%= @name %>/*', 6 | responseText: model 7 | }); 8 | 9 | $.mockjax({ 10 | url: '/<%= @name %>', 11 | type: 'POST', 12 | responseText: model 13 | }); 14 | 15 | $.mockjax({ 16 | url: '/<%= @name %>', 17 | type: 'GET', 18 | proxy: app.project_path + '/fixtures/<%= @name %>/<%= @name %>.json' 19 | }); 20 | } -------------------------------------------------------------------------------- /lib/choco/generators/templates/fixtures/model/array.js: -------------------------------------------------------------------------------- 1 | [ 2 | { "id": 1, "title": "Hello Choco!" }, 3 | { "id": 2, "title": "Hello Sammy!" } 4 | ] -------------------------------------------------------------------------------- /lib/choco/generators/templates/help/commands: -------------------------------------------------------------------------------- 1 | 2 | The choco script responds to the following commands and options : 3 | 4 | --watch 5 | The Choco watch command will track JS files in your project (create, update, remove). 6 | It will regenerate your bundled.js at every modification in one of your JS files. 7 | Your Jimfile will also be maintained by this command. 8 | 9 | generate [options] 10 | Available generators are : controller, model, scaffold, fixture, layout, plugin 11 | Run '$ choco generate', for more information. 12 | 13 | server [options] 14 | -s, --server SERVER serve using SERVER (webrick/mongrel) 15 | -p, --port PORT use PORT (default: 9292) 16 | 17 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/help/generators: -------------------------------------------------------------------------------- 1 | 2 | The following Choco generators are available : 3 | 4 | controller 5 | $ choco generate controller 6 | 7 | It will create the controller file and a views folder. 8 | 9 | model 10 | $ choco generate model 11 | 12 | It will create the model file. 13 | 14 | scaffold 15 | $ choco generate scaffold [field1 field2 ...] 16 | 17 | It will create a RESTful controller, the model and the views. 18 | 19 | scaffold 20 | $ choco generate scaffold 21 | 22 | It will create a RESTful controller, the model and the views. 23 | The fields will be determined from the JSON returned by your URL 24 | e.g : http://localhost:3000/posts.json 25 | 26 | fixture 27 | $ choco generate fixture 28 | 29 | It will create fixture & JSON files. 30 | resource_name will be pluralized. 31 | 32 | $ choco generate fixture post 33 | Will mock requests sent to /posts 34 | 35 | layout 36 | $ choco generate layout 37 | 38 | It will create the layout file. 39 | 40 | plugin 41 | $ choco generate plugin 42 | 43 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/helpers/application_helper.js: -------------------------------------------------------------------------------- 1 | // Helpers are available in your controllers and your views 2 | 3 | var ApplicationHelper = { 4 | helloWorld: function() { 5 | alert("Hello World!"); 6 | } 7 | } -------------------------------------------------------------------------------- /lib/choco/generators/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /lib/choco/generators/templates/lib/application.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | }); -------------------------------------------------------------------------------- /lib/choco/generators/templates/lib/plugin.js: -------------------------------------------------------------------------------- 1 | // js-model plugin 2 | // It allows you to add methods to all your models. 3 | 4 | var <%= @plugin_name %>Plugin = { 5 | modelsClassMethods : { 6 | myhelper: function() { 7 | alert('hello world [class method]'); 8 | } 9 | }, 10 | 11 | modelsInstanceMethods : { 12 | myhelper: function() { 13 | alert('hello world [instance method]'); 14 | } 15 | } 16 | }; 17 | 18 | // Apply the js-model plugin 19 | ChocoUtils.modelPlugin(<%= @plugin_name %>Plugin); 20 | 21 | 22 | // Sammy plugin 23 | // It allows you to add helper methods to your controllers and your views. 24 | 25 | (function($) { 26 | 27 | Sammy = Sammy || {}; 28 | 29 | Sammy.<%= @plugin_name %> = function() { 30 | 31 | this.helper('<%= @plugin_name.underscore %>', function() { 32 | alert('hello world [sammy plugin]'); 33 | }); 34 | 35 | }; 36 | 37 | })(jQuery); 38 | 39 | // Add this line to your application_controller.js to use this plugin : 40 | // this.use(Sammy.<%= @plugin_name %>); -------------------------------------------------------------------------------- /lib/choco/generators/templates/models/base.js: -------------------------------------------------------------------------------- 1 | var <%= @model_name %> = Model("<%= @model %>", { 2 | persistence: Model.RestPersistence("/<%= @route_path %>"), 3 | 4 | // Class methods 5 | }, { 6 | // Instance methods 7 | }); -------------------------------------------------------------------------------- /lib/choco/generators/templates/views/edit.template: -------------------------------------------------------------------------------- 1 |

Edit a <%= @model %>

2 |
3 | <% @keys.each do |key| -%> 4 | <% if key != 'id' -%> 5 |

6 |
7 | 8 |

9 | <% end -%> 10 | <% end -%> 11 |

12 | 13 |

14 |
-------------------------------------------------------------------------------- /lib/choco/generators/templates/views/index.template: -------------------------------------------------------------------------------- 1 |

Listing <%= @model.pluralize %>

2 | 3 | <%% if(_.isEmpty(<%= @model.pluralize %>)) { %> 4 | Sorry, we don't have any <%= @model %> yet! 5 | <%% } %> 6 | 7 | 8 | 9 | 10 | <% @keys.each do |key| -%> 11 | 12 | <% end -%> 13 | 14 | 15 | 16 | 17 | <%% $.each(<%= @model.pluralize %>, function(i, <%= @model %>) { %> 18 | 19 | <% @keys.each do |key| -%> 20 | 21 | <% end -%> 22 | 27 | 28 | <%% }); %> 29 | 30 |
<%= key.camelcase %>Actions
<%%= <%= @model %>.attr('<%= key %>') %> 23 | Permalink 24 | Edit 25 | Delete 26 |
31 | 32 |
33 |
34 | Add a new <%= @model %> -------------------------------------------------------------------------------- /lib/choco/generators/templates/views/layout.js: -------------------------------------------------------------------------------- 1 | var <%= @layout_name %>Layout = 2 | '
\ 3 | {{content}} \ 4 |
'; -------------------------------------------------------------------------------- /lib/choco/generators/templates/views/main/index.template: -------------------------------------------------------------------------------- 1 | Welcome to Choco! -------------------------------------------------------------------------------- /lib/choco/generators/templates/views/new.template: -------------------------------------------------------------------------------- 1 |

Add a new <%= @model %>

2 |
3 | <% @keys.each do |key| -%> 4 | <% if key != 'id' -%> 5 |

6 |
7 | 8 |

9 | <% end -%> 10 | <% end -%> 11 |

12 | 13 |

14 |
-------------------------------------------------------------------------------- /lib/choco/generators/templates/views/show.template: -------------------------------------------------------------------------------- 1 | <% @keys.each do |key| -%> 2 |

3 | 4 |
5 | <%%= <%= @model %>.attr('<%= key %>') %> 6 |

7 | <% end -%> 8 |
9 |
10 | Back to index -------------------------------------------------------------------------------- /spec/choco_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe "Choco" do 4 | end 5 | -------------------------------------------------------------------------------- /spec/dependency_manager_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe Choco::DependencyManager do 4 | 5 | def in_file(file, dependency, after = '') 6 | i = 0 7 | after_after = false 8 | 9 | File.new(file, "r").each do |line| 10 | after_after = true if line.include?(after) 11 | if after != '' 12 | i += 1 if after_after && line.include?(dependency) 13 | else 14 | i += 1 if line.include?(dependency) 15 | end 16 | end 17 | i 18 | end 19 | 20 | before(:each) do 21 | @project_path = 'spec/tmp/test_project' 22 | Choco::AppGenerator.start @project_path 23 | FileUtils.cd(@project_path) 24 | end 25 | 26 | after(:each) do 27 | FileUtils.cd('../../..') 28 | FileUtils.rm_rf(@project_path) 29 | end 30 | 31 | describe "adding dependencies" do 32 | 33 | describe "when it's a controller" do 34 | 35 | it "should add an entry in the Jimfile (controllers section)" do 36 | in_file('Jimfile', 'app/controllers/posts_controller').should == 0 37 | Choco::DependencyManager.add_dependency('app/controllers/posts_controller.js') 38 | in_file('Jimfile', 'app/controllers/posts_controller', "// Controllers # Don't remove this line!").should == 1 39 | end 40 | 41 | it "should configure it in the application controller" do 42 | in_file('app/controllers/application_controller.js', 'ArticlesController(this);').should == 0 43 | Choco::DependencyManager.add_dependency('app/controllers/articles_controller.js') 44 | in_file('app/controllers/application_controller.js', 45 | 'ArticlesController(this);', 46 | "/* Configure your controllers here ### Don't remove this line! */").should == 1 47 | end 48 | 49 | end 50 | 51 | describe "when it's a model" do 52 | 53 | it "should add an entry in the Jimfile (models section)" do 54 | in_file('Jimfile', 'app/models/post').should == 0 55 | Choco::DependencyManager.add_dependency('app/models/post.js') 56 | in_file('Jimfile', 'app/models/post', "// Models # Don't remove this line!").should == 1 57 | end 58 | 59 | end 60 | 61 | describe "when it's a helper" do 62 | 63 | it "should add an entry in the Jimfile (helpers section)" do 64 | in_file('Jimfile', 'app/helpers/test').should == 0 65 | Choco::DependencyManager.add_dependency('app/helpers/test.js') 66 | in_file('Jimfile', 'app/helpers/test', "// Helpers # Don't remove this line!").should == 1 67 | end 68 | 69 | end 70 | 71 | describe "when it's a fixture" do 72 | 73 | it "should add an entry in the Jimfile (models section)" do 74 | in_file('Jimfile', 'fixtures/posts').should == 0 75 | Choco::DependencyManager.add_dependency('fixtures/posts.js') 76 | in_file('Jimfile', 'fixtures/posts', "// Fixtures # Don't remove this line!").should == 1 77 | end 78 | 79 | end 80 | 81 | describe "when it's a lib" do 82 | 83 | it "should add an entry in the Jimfile (libs section)" do 84 | in_file('Jimfile', 'lib/my_lib').should == 0 85 | Choco::DependencyManager.add_dependency('lib/my_lib.js') 86 | in_file('Jimfile', 'lib/my_lib', "// Libs # Don't remove this line!").should == 1 87 | 88 | in_file('Jimfile', 'another/file').should == 0 89 | Choco::DependencyManager.add_dependency('another/file.js') 90 | in_file('Jimfile', 'another/file', "// Libs # Don't remove this line!").should == 1 91 | end 92 | 93 | end 94 | 95 | end 96 | 97 | describe "removing dependencies" do 98 | 99 | it "should remove the dependency from the jimfile" do 100 | Choco::DependencyManager.add_dependency('app/models/post.js') 101 | in_file('Jimfile', 'app/models/post').should == 1 102 | Choco::DependencyManager.remove_dependency('app/models/post.js') 103 | in_file('Jimfile', 'app/models/post').should == 0 104 | end 105 | 106 | describe "when it's a controller" do 107 | it "should remove the configuration from the application controller" do 108 | Choco::DependencyManager.add_dependency('app/controllers/articles_controller.js') 109 | in_file('app/controllers/application_controller.js', 'ArticlesController(this);').should == 1 110 | Choco::DependencyManager.remove_dependency('app/controllers/articles_controller.js') 111 | in_file('app/controllers/application_controller.js', 'ArticlesController(this);').should == 0 112 | end 113 | end 114 | 115 | describe "when it's a model" do 116 | it "should not change empty the application controller" do 117 | Choco::DependencyManager.remove_dependency('app/models/article.js') 118 | in_file('app/controllers/application_controller.js', 'var app = $.sammy(function() {').should == 1 119 | end 120 | end 121 | 122 | end 123 | end -------------------------------------------------------------------------------- /spec/generators_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe "Generators" do 4 | 5 | describe Choco::AppGenerator do 6 | 7 | before(:all) do 8 | @project_path = 'spec/tmp/test_project' 9 | Choco::AppGenerator.start @project_path 10 | end 11 | 12 | after(:all) do 13 | FileUtils.rm_rf @project_path 14 | end 15 | 16 | it "should generate the project folder" do 17 | File.exist?(@project_path).should be_true 18 | end 19 | 20 | it "should generate the app folder" do 21 | File.exist?(@project_path + '/app').should be_true 22 | File.exist?(@project_path + '/app/controllers').should be_true 23 | File.exist?(@project_path + '/app/helpers').should be_true 24 | File.exist?(@project_path + '/app/models').should be_true 25 | File.exist?(@project_path + '/app/views').should be_true 26 | File.exist?(@project_path + '/app/controllers/application_controller.js').should be_true 27 | File.exist?(@project_path + '/app/views/main/index.template').should be_true 28 | File.exist?(@project_path + '/app/helpers/application_helper.js').should be_true 29 | end 30 | 31 | it "should generate the lib folder" do 32 | File.exist?(@project_path + '/lib').should be_true 33 | File.exist?(@project_path + '/lib/application.js').should be_true 34 | end 35 | 36 | it "should generate the images folder" do 37 | File.exist?(@project_path + '/images').should be_true 38 | end 39 | 40 | it "should generate the stylesheets folder" do 41 | File.exist?(@project_path + '/stylesheets').should be_true 42 | File.exist?(@project_path + '/stylesheets/application.css').should be_true 43 | end 44 | 45 | it "should generate the fixtures folder" do 46 | File.exist?(@project_path + '/fixtures').should be_true 47 | end 48 | 49 | it "should generate the spec folder" do 50 | File.exist?(@project_path + '/spec').should be_true 51 | end 52 | 53 | it "should generate the script folder" do 54 | File.exist?(@project_path + '/script').should be_true 55 | File.exist?(@project_path + '/script/choco').should be_true 56 | end 57 | 58 | it "should generate the main files" do 59 | File.exist?(@project_path + '/Jimfile').should be_true 60 | File.exist?(@project_path + '/Rakefile').should be_true 61 | File.exist?(@project_path + '/README.rdoc').should be_true 62 | File.exist?(@project_path + '/index.html').should be_true 63 | end 64 | 65 | describe "templates" do 66 | 67 | describe "index.html" do 68 | 69 | before(:all) do 70 | @file = get_file_as_string(@project_path + '/index.html') 71 | end 72 | 73 | it "should include the bundled.js file" do 74 | @file.include?('').should be_true 75 | end 76 | 77 | it "should include the root div" do 78 | @file.include?('
').should be_true 79 | end 80 | 81 | end 82 | 83 | end 84 | end 85 | 86 | describe "code generators" do 87 | 88 | before(:all) do 89 | @project_path = 'spec/tmp/test_project' 90 | Choco::AppGenerator.start @project_path 91 | FileUtils.cd(@project_path) 92 | end 93 | 94 | after(:all) do 95 | FileUtils.cd('..') 96 | FileUtils.rm_rf('test_project') 97 | FileUtils.cd('../..') 98 | end 99 | 100 | describe Choco::ControllerGenerator do 101 | 102 | it "should generate a controller file and a views folder" do 103 | Choco::ControllerGenerator.start 'posts' 104 | File.exist?('app/controllers/posts_controller.js').should be_true 105 | File.exist?('app/views/posts').should be_true 106 | end 107 | 108 | it "should not pluralize the controller name" do 109 | Choco::ControllerGenerator.start 'singular' 110 | File.exist?('app/controllers/singular_controller.js').should be_true 111 | end 112 | 113 | it "should underscore the name" do 114 | Choco::ControllerGenerator.start 'UnderScore' 115 | File.exist?('app/controllers/under_score_controller.js').should be_true 116 | end 117 | 118 | describe "templates" do 119 | 120 | before(:all) do 121 | Choco::ControllerGenerator.start 'UnderScore' 122 | @file = get_file_as_string('app/controllers/under_score_controller.js') 123 | end 124 | 125 | describe "the generated controller" do 126 | it "should be named correctly" do 127 | @file.include?('UnderScoreController').should be_true 128 | end 129 | 130 | it "should define an index route" do 131 | @file.include?("get('#/under_score'").should be_true 132 | end 133 | end 134 | end 135 | end 136 | 137 | describe Choco::ModelGenerator do 138 | it "should generate a model file" do 139 | Choco::ModelGenerator.start 'post' 140 | File.exist?('app/models/post.js').should be_true 141 | end 142 | 143 | it "should generate a model file with a singular name" do 144 | Choco::ModelGenerator.start 'posts' 145 | File.exist?('app/models/post.js').should be_true 146 | end 147 | 148 | describe "templates" do 149 | 150 | describe "the generated model" do 151 | 152 | before(:all) do 153 | Choco::ModelGenerator.start 'post' 154 | @file = get_file_as_string('app/models/post.js') 155 | end 156 | 157 | it "should be named correctly" do 158 | @file.include?('var Post = Model("post"').should be_true 159 | end 160 | 161 | it "model persistence URL should be correct" do 162 | @file.include?('Model.RestPersistence("/posts")').should be_true 163 | end 164 | end 165 | 166 | end 167 | end 168 | 169 | describe Choco::LayoutGenerator do 170 | it "should generate a layout file" do 171 | Choco::LayoutGenerator.start 'main' 172 | File.exist?('app/views/layouts/main.js').should be_true 173 | end 174 | 175 | describe "templates" do 176 | 177 | it "the layout should be named correctly" do 178 | Choco::LayoutGenerator.start 'main' 179 | file = get_file_as_string('app/views/layouts/main.js') 180 | file.include?('var MainLayout').should be_true 181 | end 182 | 183 | end 184 | 185 | end 186 | 187 | describe Choco::FixtureGenerator do 188 | it "should generate a fixture file" do 189 | Choco::FixtureGenerator.start 'posts' 190 | File.exist?('fixtures/posts.js').should be_true 191 | end 192 | 193 | it "should generate a fixture folder" do 194 | Choco::FixtureGenerator.start 'posts' 195 | File.exist?('fixtures/posts').should be_true 196 | File.exist?('fixtures/posts/posts.json').should be_true 197 | end 198 | 199 | describe "templates" do 200 | 201 | it "the URL should have a correct format" do 202 | Choco::FixtureGenerator.start 'post' 203 | file = get_file_as_string('fixtures/posts.js') 204 | file.include?("url: '/posts',").should be_true 205 | end 206 | 207 | end 208 | 209 | end 210 | 211 | describe Choco::PluginGenerator do 212 | it "should generate a plugin file" do 213 | Choco::PluginGenerator.start 'live' 214 | File.exist?('lib/plugin_live.js').should be_true 215 | end 216 | 217 | describe "templates" do 218 | 219 | describe "the generated plugin" do 220 | 221 | before(:all) do 222 | Choco::PluginGenerator.start 'live' 223 | @file = get_file_as_string('lib/plugin_live.js') 224 | end 225 | 226 | it "should be named correctly" do 227 | @file.include?('var LivePlugin').should be_true 228 | end 229 | 230 | it "should apply the js-model plugin" do 231 | @file.include?('ChocoUtils.modelPlugin(LivePlugin);').should be_true 232 | end 233 | 234 | it "should display the syntax to activate the sammy plugin" do 235 | @file.include?('// this.use(Sammy.Live);').should be_true 236 | end 237 | 238 | end 239 | 240 | end 241 | end 242 | 243 | describe Choco::ScaffoldGenerator do 244 | describe "with a list of fields" do 245 | it "should generate a scaffold based on the list of fields given as argument" do 246 | gen = Choco::ScaffoldGenerator.new(['post', ['author', 'title', 'content']]) 247 | gen.invoke 248 | 249 | File.exist?('app/controllers/posts_controller.js').should be_true 250 | File.exist?('app/models/post.js').should be_true 251 | File.exist?('app/views/posts/index.template').should be_true 252 | File.exist?('app/views/posts/show.template').should be_true 253 | File.exist?('app/views/posts/new.template').should be_true 254 | File.exist?('app/views/posts/edit.template').should be_true 255 | end 256 | end 257 | 258 | describe "with a URL" do 259 | it "should generate a scaffold based on the fields present in the JSON response" do 260 | url = 'http://localhost:3000/posts' 261 | gen = Choco::ScaffoldGenerator.new(['post', url]) 262 | json = [{"title" => "Please, contribute!","author" => "antho","content" => "What do you think about Choco?"}] 263 | 264 | gen.should_receive(:open).with(url, 'UserAgent' => 'Choco').and_return(StringIO.new) 265 | JSON.should_receive(:parse).and_return(json) 266 | 267 | gen.invoke 268 | 269 | gen.keys.should == ["author", "title", "content"] 270 | File.exist?('app/controllers/posts_controller.js').should be_true 271 | File.exist?('app/models/post.js').should be_true 272 | File.exist?('app/views/posts/index.template').should be_true 273 | File.exist?('app/views/posts/show.template').should be_true 274 | File.exist?('app/views/posts/new.template').should be_true 275 | File.exist?('app/views/posts/edit.template').should be_true 276 | end 277 | end 278 | 279 | describe "templates" do 280 | 281 | before(:each) do 282 | gen = Choco::ScaffoldGenerator.new(['post', ['author', 'title', 'content']]) 283 | gen.invoke 284 | end 285 | 286 | it "the index file should have a column for each field" do 287 | file = get_file_as_string('app/views/posts/index.template') 288 | file.include?("Author").should be_true 289 | file.include?("Title").should be_true 290 | file.include?("Content").should be_true 291 | file.include?("<%= post.attr('author') %>").should be_true 292 | file.include?("<%= post.attr('title') %>").should be_true 293 | file.include?("<%= post.attr('content') %>").should be_true 294 | end 295 | 296 | it "the show file should have a label for each field" do 297 | file = get_file_as_string('app/views/posts/show.template') 298 | file.include?("").should be_true 299 | file.include?("").should be_true 300 | file.include?("").should be_true 301 | end 302 | 303 | it "the new file should have a field for each field" do 304 | file = get_file_as_string('app/views/posts/new.template') 305 | file.include?('').should be_true 306 | file.include?('').should be_true 307 | file.include?('').should be_true 308 | end 309 | 310 | it "the edit file should have a field for each field" do 311 | file = get_file_as_string('app/views/posts/edit.template') 312 | file.include?(%Q()).should be_true 313 | file.include?(%Q()).should be_true 314 | file.include?(%Q()).should be_true 315 | end 316 | 317 | end 318 | end 319 | end 320 | end 321 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 2 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 3 | require 'rubygems' 4 | require 'choco' 5 | require 'spec' 6 | require 'spec/autorun' 7 | 8 | def get_file_as_string(filename) 9 | data = '' 10 | f = File.open(filename, "r") 11 | f.each_line { |line| data += line } 12 | data 13 | end 14 | 15 | Spec::Runner.configure do |config| 16 | 17 | end 18 | --------------------------------------------------------------------------------