Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added Basic extensions for ActiveRecord, ActionView, ActionController…

… and Haml
  • Loading branch information...
commit bd25db079201ecbef07567573091b51eb4b22892 1 parent a9df73f
Alexander Semyonov authored
View
8 README.rdoc
@@ -1,9 +1,9 @@
-= espresso
+= Espresso
-Description goes here.
+Some useful extensions to default Rails environment.
== Note on Patches/Pull Requests
-
+
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
@@ -15,4 +15,4 @@ Description goes here.
== Copyright
-Copyright (c) 2009 Alexander Semyonov. See LICENSE for details.
+© 2009 Alexander Semyonov. See LICENSE for details.
View
13 Rakefile
@@ -10,11 +10,12 @@ begin
gem.email = 'rotuka@tokak.ru'
gem.homepage = 'http://github.com/krasivotokak/espresso'
gem.authors = ['Alexander Semyonov']
- gem.add_dependency('searchlogic', '>= 2.2.3')
- gem.add_dependency('inherited_resources', '>= 0.8.5')
- gem.add_dependency('will_paginate', '>= 2.3.11')
- gem.add_dependency('formtastic', '>= 0.9.7')
+ gem.add_dependency('activesupport', '~> 2.3.5')
+ gem.add_dependency('activerecord', '~> 2.3.5')
+ gem.add_dependency('actionpack', '~> 2.3.5')
+ gem.add_dependency('inherited_resources', '~> 1.0.5')
gem.add_development_dependency('shoulda')
+ gem.add_development_dependency('redgreen')
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
@@ -48,7 +49,9 @@ task :default => :test
begin
require 'yard'
- YARD::Rake::YardocTask.new
+ YARD::Rake::YardocTask.new do |yard|
+ yard.options << '--no-private'
+ end
rescue LoadError
task :yardoc do
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
View
70 lib/espresso.rb
@@ -1,2 +1,72 @@
+$KCODE = 'u'
+
module Espresso
+ autoload :Model, 'espresso/model'
+ autoload :View, 'espresso/view'
+ autoload :Controller, 'espresso/controller'
+
+ BASE_MODULES = %w(model view controller)
+
+ # Configures Espresso.
+ # By default, loads all extensions
+ def self.configure
+ if block_given?
+ yield
+ else
+ uses :all
+ end
+ end
+
+ # Loads Espresso extensions
+ # @param [String, Symbol] extension name of the Espresso extension
+ # @param [true, false] extend_modules whether to extend modules Model, View, Controller or not
+ def self.uses(extension, extend_modules = true)
+ Kernel.require "espresso/extensions/#{extension}"
+ if extend_modules && extension != :all
+ extension = extension.to_s.classify
+ BASE_MODULES.each do |module_name|
+ mod = const_get(module_name.classify)
+ extend_module(mod, mod.const_get(extension))
+ end
+ end
+ end
+
+ # Extend module InstanceMethods and ClassMethods with extension‘s one
+ # @param [Module] mod module to extend
+ # @param [Module] extension module, containing extensions
+ def self.extend_module(mod, extension)
+ mod.const_get(:ClassMethods).send(:include,
+ extension.const_get(:ClassMethods))
+ mod.const_get(:InstanceMethods).send(:include,
+ extension.const_get(:InstanceMethods))
+ end
+
+ # Requires Espressso extension and submodules
+ # @param [String, Symbol] library extension name to require
+ # @param [Array, nil] submodules list of submodules to include
+ def self.require(library, submodules = BASE_MODULES)
+ Kernel.require(library)
+ submodules.each do |module_name|
+ Kernel.require("espresso/#{module_name}/#{library}")
+ end
+ Kernel.require("espresso/")
+ rescue LoadError
+ raise "Espresso require #{library} to use Espresso::#{library.classify} extension"
+ end
+end
+
+if defined?(ActiveRecord)
+ Espresso.uses :active_record, false
+end
+
+if defined?(ActionView)
+ Espresso.uses :action_view, false
+end
+
+if defined?(ActionController)
+ Espresso.uses :action_controller, false
+end
+
+if defined?(Haml)
+ Espresso.uses :haml, false
end
View
16 lib/espresso/controller.rb
@@ -0,0 +1,16 @@
+module Espresso
+ module Controller
+ def self.included(base)
+ base.extend ClassMethods
+ base.class_eval do
+ include InstanceMethods
+ end
+ end
+
+ module ClassMethods
+ end
+
+ module InstanceMethods
+ end
+ end
+end
View
6 lib/espresso/extensions/action_controller.rb
@@ -0,0 +1,6 @@
+require 'action_controller'
+require 'espresso/controller'
+
+class ActionController::Base
+ include Espresso::Controller
+end
View
6 lib/espresso/extensions/action_view.rb
@@ -0,0 +1,6 @@
+require 'action_view'
+require 'espresso/view'
+
+class ActionView::Base
+ include Espresso::View
+end
View
6 lib/espresso/extensions/active_record.rb
@@ -0,0 +1,6 @@
+require 'active_record'
+require 'espresso/model'
+
+class ActiveRecord::Base
+ include Espresso::Model
+end
View
28 lib/espresso/extensions/haml.rb
@@ -0,0 +1,28 @@
+require 'haml'
+require 'haml/buffer'
+require 'espresso/view'
+
+class Haml::Buffer
+ include Espresso::View
+ # Takes an array of objects and uses the class and id of the first
+ # one to create an attributes hash.
+ # The second object, if present, is used as a prefix,
+ # just like you can do with `dom_id()` and `dom_class()` in Rails
+ def parse_object_ref(ref)
+ prefix = ref[1]
+ ref = ref[0]
+ # Let's make sure the value isn't nil. If it is, return the default Hash.
+ return {} if ref.nil?
+ class_name = underscore(ref.class)
+ id = "#{class_name}_#{ref.id || 'new'}"
+
+ if ref.respond_to?(:model_modifiers)
+ class_name = model_classes(ref)
+ elsif prefix
+ class_name = "#{prefix}_#{class_name}"
+ id = "#{prefix}_#{id}"
+ end
+
+ {'id' => id, 'class' => class_name}
+ end
+end
View
35 lib/espresso/model.rb
@@ -0,0 +1,35 @@
+require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/string'
+
+module Espresso
+ module Model
+ # @private
+ def self.included(base) #:nodoc:
+ base.extend ClassMethods
+ base.class_eval do
+ include InstanceMethods
+
+ class_inheritable_accessor :name_field, :model_modifiers
+
+ self.name_field = :name
+ self.model_modifiers = []
+ end
+ end
+
+ module ClassMethods
+ # Make a slug from object‘s #name_field
+ # @param [ActiveRecord::Base] model object, which slug is making
+ # @return [String] slug made from model’s #name_field
+ def make_slug(model)
+ model.send(name_field).parameterize
+ end
+ end
+
+ module InstanceMethods
+ # String representation of model, based on Model’s #name_field
+ def to_s
+ send(self.class.name_field)
+ end
+ end
+ end
+end
View
52 lib/espresso/view.rb
@@ -0,0 +1,52 @@
+require 'active_support/core_ext/module'
+
+module Espresso
+ module View
+ mattr_accessor :block_prefix
+ self.block_prefix = 'b'
+
+ # @private
+ def self.included(base)
+ base.extend ClassMethods
+ base.send(:include, InstanceMethods)
+ end
+
+ module ClassMethods
+ # Generic espresso block classes
+ # @param [String, Symbol] main_class main block class
+ # @param [Array] modifiers array of modifiers for main block class
+ # @param [Hash] options another options
+ # @option options [String] :type (Espresso::View.block_prefix) type of the block
+ def espresso_block_classes(main_class, modifiers = [], options = {})
+ options ||= {}
+ options[:type] ||= Espresso::View.block_prefix
+
+ main_class = "#{options[:type]}-#{main_class}"
+ [main_class].tap do |classes|
+ modifiers.each do |modifier|
+ classes << "#{main_class}_#{modifier}"
+ end
+ end.join(' ')
+ end
+ end
+
+ module InstanceMethods
+ # Model‘s classes, based on Model.model_modifiers
+ # @param [ActiveRecord::Base] model model to build classes from
+ def model_classes(model)
+ klass = model.class
+ main_class = klass.name.underscore.gsub(/(_|\/)/, '-')
+ modifiers = if klass.respond_to?(:model_modifiers)
+ klass.model_modifiers.find_all do |modifier|
+ method = "#{modifier}?"
+ model.respond_to?(method) && model.send(method)
+ end
+ else
+ []
+ end
+ self.class.espresso_block_classes(main_class,
+ modifiers)
+ end
+ end
+ end
+end
View
1  test/espresso_controller_test.rb
@@ -0,0 +1 @@
+require 'test_helper'
View
26 test/espresso_model_test.rb
@@ -0,0 +1,26 @@
+require 'test_helper'
+require 'example_model'
+
+class ExampleModelTest < Test::Unit::TestCase
+ should_have_class_methods :make_slug
+ should_have_instance_methods :to_s
+
+ {
+ 'Foo Bar' => 'foo-bar',
+ 'Baz Bar Foo' => 'baz-bar-foo'
+ }.each do |name, slug|
+ context "Example instance with name '#{name}'" do
+ setup {
+ @example = ExampleModel.new
+ @example.name = name
+ }
+
+ should "represents to_s as '#{name}'" do
+ assert_equal name, @example.to_s
+ end
+ should "make slug '#{slug}'" do
+ assert_equal slug, ExampleModel.make_slug(@example)
+ end
+ end
+ end
+end
View
5 test/espresso_test.rb
@@ -0,0 +1,5 @@
+require 'test_helper'
+
+class EspressoTest < Test::Unit::TestCase
+ should_have_class_methods :configure, :uses, :extend_module, :require
+end
View
42 test/espresso_view_test.rb
@@ -0,0 +1,42 @@
+require 'test_helper'
+require 'espresso/view'
+
+class Espresso::ViewTest < Test::Unit::TestCase
+ should_have_class_methods :block_prefix
+
+ include Espresso::View
+
+ {
+ 'b-example' => ['example'],
+ 'b-example b-example_foo' => ['example', %w(foo)],
+ 'b-example b-example_foo b-example_bar' => ['example', %w(foo bar)],
+ 'o-example' => ['example', [], {:type => 'o'}],
+ 'y-example y-example_foo y-example_bar y-example_baz' => ['example', %w(foo bar baz), {:type => 'y'}],
+ }.each do |result, params|
+ should "build “#{result}” from #{params.inspect}" do
+ assert_equal result, self.class.espresso_block_classes(*params)
+ end
+ end
+
+ class Example
+ def self.model_modifiers
+ [:foo]
+ end
+ end
+
+ class ::FooExample < Example
+ def foo?
+ true
+ end
+ end
+
+ should 'build “b-espresso-view-test-example” from Example instance' do
+ @example = Example.new
+ assert_equal 'b-espresso-view-test-example', model_classes(@example)
+ end
+
+ should 'build “b-foo-example b-foo-example_foo” from FooExample instance' do
+ @example = FooExample.new
+ assert_equal 'b-foo-example b-foo-example_foo', model_classes(@example)
+ end
+end
View
23 test/example_model.rb
@@ -0,0 +1,23 @@
+require 'espresso/model'
+
+class ExampleModel
+ include Espresso::Model
+ self.model_modifiers << :safe
+
+ attr_accessor :id, :name, :safe
+
+ def initialize(id = nil, safe = false)
+ self.id = id
+ self.safe = safe
+ end
+
+ def safe?
+ safe == true
+ end
+end
+
+class NonModel
+ def id
+ 6
+ end
+end
View
19 test/haml_buffer_test.rb
@@ -0,0 +1,19 @@
+require 'test_helper'
+require 'espresso/extensions/haml'
+require 'example_model'
+
+class Haml::BufferTest < Test::Unit::TestCase
+ context 'Haml::Buffer instance' do
+ setup { @buffer = Haml::Buffer.new }
+
+ {
+ [ExampleModel.new(1)] => {'id' => 'example_model_1', 'class' => 'b-example-model'},
+ [NonModel.new, :prefix] => {'id' => 'prefix_non_model_6', 'class' => 'prefix_non_model'},
+ [ExampleModel.new(5, true)] => {'id' => 'example_model_5', 'class' => 'b-example-model b-example-model_safe'}
+ }.each do |ref, result|
+ should "parse #{ref.inspect} ref to #{result.inspect} " do
+ assert_equal(result, @buffer.parse_object_ref(ref))
+ end
+ end
+ end
+end
View
31 test/test_helper.rb
@@ -1,10 +1,39 @@
require 'rubygems'
require 'test/unit'
require 'shoulda'
+require 'redgreen'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'espresso'
-class Test::Unit::TestCase
+require 'active_support/core_ext/string'
+module Test
+ module Unit
+ class TestCase
+ def self.should_have_class_methods(*methods)
+ get_options!(methods)
+ klass = described_type
+ methods.each do |method|
+ should "respond to class method ##{method}" do
+ assert_respond_to(klass, method, "#{klass.name} does not have class method #{method}")
+ end
+ end
+ end
+
+ def self.should_have_instance_methods(*methods)
+ get_options!(methods)
+ klass = described_type
+ methods.each do |method|
+ should "respond to instance method ##{method}" do
+ assert_respond_to(klass.new, method, "#{klass.name} does not have instance method #{method}")
+ end
+ end
+ end
+ end
+
+ class TestCaseTest < TestCase
+ should_have_class_methods :should_have_class_methods, :should_have_instance_methods
+ end
+ end
end
View
4 test/unit/espresso_test.rb
@@ -1,4 +0,0 @@
-require 'test_helper'
-
-class EspressoTest < Test::Unit::TestCase
-end
Please sign in to comment.
Something went wrong with that request. Please try again.