diff --git a/.gitignore b/.gitignore index 2aad4fdd..d1c88127 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ pkg *.gemspec rails_3_gems doc/performance.txt +doc/production.sh diff --git a/Gemfile b/Gemfile index fef8cf72..31285842 100644 --- a/Gemfile +++ b/Gemfile @@ -11,20 +11,14 @@ gem "mongoid", ">= 2.0.0.beta6" gem "mongoid_acts_as_tree", ">= 0.1.2" gem "warden" gem "devise", ">= 1.1.rc0" -gem "haml", '>= 3.0.1' -# gem "rmagick" +gem "haml", ">= 3.0.1" +gem "rmagick", "2.12.2" gem "aws" gem "jeweler" gem "mimetype-fu", :require => "mimetype_fu" gem "formtastic-rails3", :require => "formtastic" gem "carrierwave-rails3", :require => "carrierwave" -# gem 'formtastic-rails3', :require => 'formtastic', :path => 'rails_3_gems/formtastic' -# gem "formtastic", :git => 'git://github.com/justinfrench/formtastic.git', :branch => 'rails3' -# gem "carrierwave", :git => "http://github.com/jnicklas/carrierwave.git" -# gem "carrierwave", :path => 'rails_3_gems/carrierwave' - - # Development environment group :development do # Using mongrel instead of webrick (default server) diff --git a/VERSION b/VERSION index dc9a74ed..6acbf68f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1.3 \ No newline at end of file +0.0.1.4 \ No newline at end of file diff --git a/app/controllers/admin/content_types_controller.rb b/app/controllers/admin/content_types_controller.rb index 5b6f7ac5..597b8d7d 100644 --- a/app/controllers/admin/content_types_controller.rb +++ b/app/controllers/admin/content_types_controller.rb @@ -45,7 +45,7 @@ module Admin flash[:error] = e.to_s end - redirect_to admin_content_types_url + redirect_to admin_pages_url end end diff --git a/app/helpers/admin/custom_fields_helper.rb b/app/helpers/admin/custom_fields_helper.rb index 1c2dc7e1..cdadbeea 100644 --- a/app/helpers/admin/custom_fields_helper.rb +++ b/app/helpers/admin/custom_fields_helper.rb @@ -2,7 +2,7 @@ module Admin::CustomFieldsHelper def options_for_field_kind(selected = nil) # %w{String Text Boolean Email File Date} - options = %w{String Text}.map do |kind| + options = %w{String Text Select}.map do |kind| [t("admin.custom_fields.kind.#{kind.downcase}"), kind] end end diff --git a/app/models/asset.rb b/app/models/asset.rb index 8396dadc..8ace9218 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -1,10 +1,11 @@ class Asset include Mongoid::Document - include Mongoid::Timestamps + include Mongoid::Timestamps ## Extensions ## include Models::Extensions::Asset::Vignette + include CustomFields::ProxyClassEnabler ## fields ## field :name, :type => String diff --git a/app/models/theme_asset.rb b/app/models/theme_asset.rb index 8e4a4b91..c29aa08f 100644 --- a/app/models/theme_asset.rb +++ b/app/models/theme_asset.rb @@ -53,6 +53,8 @@ class ThemeAsset end def performing_plain_text? + return true if !self.new_record? && !self.image? && self.errors.empty? + !(self.performing_plain_text.blank? || self.performing_plain_text == 'false' || self.performing_plain_text == false) end diff --git a/app/uploaders/asset_uploader.rb b/app/uploaders/asset_uploader.rb index 32eb5288..21250395 100644 --- a/app/uploaders/asset_uploader.rb +++ b/app/uploaders/asset_uploader.rb @@ -35,8 +35,8 @@ class AssetUploader < CarrierWave::Uploader::Base self.class.content_types.each_pair do |type, rules| rules.each do |rule| case rule - when String then value = type if file.content_type == rule - when Regexp then value = type if (file.content_type =~ rule) == 0 + when String then value = type if content_type == rule + when Regexp then value = type if (content_type =~ rule) == 0 end end end diff --git a/app/views/admin/content_types/_form.html.haml b/app/views/admin/content_types/_form.html.haml index ddf2b629..a2bb4420 100644 --- a/app/views/admin/content_types/_form.html.haml +++ b/app/views/admin/content_types/_form.html.haml @@ -1,13 +1,15 @@ - content_for :head do - = javascript_include_tag 'admin/custom_fields' + = javascript_include_tag 'admin/plugins/fancybox', 'admin/custom_fields' + = stylesheet_link_tag 'admin/plugins/fancybox', 'admin/box' = f.inputs :name => :information do = f.input :name = f.input :slug = f.input :description -= render 'admin/shared/custom_fields', :f => f, :collection_name => 'contents' += render 'admin/custom_fields/index', :f => f, :collection_name => 'contents' = f.foldable_inputs :name => :options, :class => 'switchable' do = f.input :highlighted_field_name, :as => :select, :collection => options_for_highlighted_field(f.object, 'contents'), :include_blank => false - = f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false \ No newline at end of file + = f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false + diff --git a/app/views/admin/content_types/edit.html.haml b/app/views/admin/content_types/edit.html.haml index 1adda3e2..4f9e0e2a 100644 --- a/app/views/admin/content_types/edit.html.haml +++ b/app/views/admin/content_types/edit.html.haml @@ -13,4 +13,6 @@ = render 'form', :f => form - = render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update \ No newline at end of file + = render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update + += render 'admin/custom_fields/edit' \ No newline at end of file diff --git a/app/views/admin/custom_fields/_edit.html.haml b/app/views/admin/custom_fields/_edit.html.haml new file mode 100644 index 00000000..93234e17 --- /dev/null +++ b/app/views/admin/custom_fields/_edit.html.haml @@ -0,0 +1,8 @@ +.box-wrapper + #edit-custom-field + %h2= t('.title') + + = form_tag '#', :class => 'formtastic' do + = fields_for CustomFields::Field.new, :builder => Formtastic::SemanticFormHelper.builder do |g| + = g.inputs :name => :information do + = g.input :_alias \ No newline at end of file diff --git a/app/views/admin/custom_fields/_index.html.haml b/app/views/admin/custom_fields/_index.html.haml new file mode 100644 index 00000000..70da0432 --- /dev/null +++ b/app/views/admin/custom_fields/_index.html.haml @@ -0,0 +1,53 @@ +- collection_name = "#{collection_name.singularize}_custom_fields" +- custom_fields = f.object.send(collection_name.to_sym) +- ordered_custom_fields = f.object.send(:"ordered_#{collection_name}") + += f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields off' do + - ordered_custom_fields.each do |field| + = f.fields_for collection_name.to_sym, field, :child_index => field._index do |g| + %li{ :class => "item added #{'error' unless field.errors.empty?}"} + %span.handle + = image_tag 'admin/form/icons/drag.png' + + = g.hidden_field :position, :class => 'position' + + = g.hidden_field :_alias, :class => 'alias' + + = g.text_field :label + + — + + %em= t("admin.custom_fields.kind.#{field.kind.downcase}") + + = g.select :kind, options_for_field_kind + +   + + %span.actions + = link_to image_tag('admin/form/pen.png'), '#edit-custom-field', :class => 'edit first' + = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm') + + = f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g| + %li{ :class => 'item template' } + %span.handle + = image_tag 'admin/form/icons/drag.png' + + = g.hidden_field :position, :class => 'position' + + = g.hidden_field :_alias, :class => 'alias' + + = g.text_field :label, :class => 'string label void' + + — + + %em + + = g.select :kind, options_for_field_kind + +   + + %span.actions + = link_to image_tag('admin/form/pen.png'), '#edit-custom-field', :class => 'edit first' + = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm') + %button{ :class => 'button light add', :type => 'button' } + %span= t('admin.buttons.new_item') diff --git a/app/views/admin/shared/_custom_fields.html.haml b/app/views/admin/shared/_custom_fields.html.haml index b30b77ba..cb08a662 100644 --- a/app/views/admin/shared/_custom_fields.html.haml +++ b/app/views/admin/shared/_custom_fields.html.haml @@ -10,6 +10,8 @@ = image_tag 'admin/form/icons/drag.png' = g.hidden_field :position, :class => 'position' + + = g.hidden_field :_alias, :class => 'alias' = g.text_field :label @@ -22,7 +24,8 @@   %span.actions - = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm') + = link_to image_tag('admin/form/pen.png'), '#', :class => 'edit first' + = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm') = f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g| %li{ :class => 'item template' } @@ -30,6 +33,8 @@ = image_tag 'admin/form/icons/drag.png' = g.hidden_field :position, :class => 'position' + + = g.hidden_field :_alias, :class => 'alias' = g.text_field :label, :class => 'string label void' @@ -42,6 +47,9 @@   %span.actions - = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm') + = link_to image_tag('admin/form/pen.png'), '#', :class => 'edit first' + = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('admin.messages.confirm') %button{ :class => 'button light add', :type => 'button' } - %span= t('admin.buttons.new_item') \ No newline at end of file + %span= t('admin.buttons.new_item') + + \ No newline at end of file diff --git a/app/views/admin/sites/_form.html.haml b/app/views/admin/sites/_form.html.haml index b3c9ea39..301880ec 100644 --- a/app/views/admin/sites/_form.html.haml +++ b/app/views/admin/sites/_form.html.haml @@ -24,7 +24,7 @@ %span.actions = link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm') - %li.item.new + %li.item.template %em http:// = text_field_tag 'label', t('formtastic.hints.site.domain_name'), :class => 'string label void' diff --git a/app/views/admin/theme_assets/images.html.haml b/app/views/admin/theme_assets/images.html.haml index 3d4db34c..1526ee34 100644 --- a/app/views/admin/theme_assets/images.html.haml +++ b/app/views/admin/theme_assets/images.html.haml @@ -6,10 +6,10 @@ - if @image_assets.empty? %p.no-items= t('.no_items') - - else - %ul.assets - = render 'asset', :asset => current_site.theme_assets.build, :edit => false - - = render :partial => 'asset', :collection => @image_assets, :locals => { :per_row => 3, :edit => false } - - %li.clear \ No newline at end of file + + %ul.assets + = render 'asset', :asset => current_site.theme_assets.build, :edit => false + + = render :partial => 'asset', :collection => @image_assets, :locals => { :per_row => 3, :edit => false } + + %li.clear \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index 448e65bf..4c2362e3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -5,6 +5,7 @@ require File.expand_path('../boot', __FILE__) require "action_controller/railtie" require "action_mailer/railtie" require "active_resource/railtie" +require "mongoid/railtie" # Auto-require default libraries and those for the current Rails environment. Bundler.require :default, Rails.env diff --git a/config/database.yml b/config/database.yml deleted file mode 100644 index 8b145ac5..00000000 --- a/config/database.yml +++ /dev/null @@ -1,20 +0,0 @@ -defaults: &defaults - host: localhost - -development: - <<: *defaults - database: locomotive_dev - -test: &test - <<: *defaults - database: locomotive_test - -production: - <<: *defaults - host: db.mongohq.com - username: user - password: pass - database: fanboy - -cucumber: - <<: *test \ No newline at end of file diff --git a/config/environments/production.rb b/config/environments/production.rb index 52bdb127..df40dde6 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -30,16 +30,4 @@ Locomotive::Application.configure do # Enable threaded mode # config.threadsafe! -end - -config.action_mailer.delivery_method = :smtp - -config.action_mailer.smtp_settings = { - :enable_starttls_auto => true, - :address => "smtp.gmail.com", - :port => 587, - :domain => "nocoffee.fr", - :authentication => :plain, - :user_name => "didier@nocoffee.fr", - :password => "pepscou" -} \ No newline at end of file +end \ No newline at end of file diff --git a/config/initializers/mongoid.rb b/config/initializers/mongoid.rb index dee177fe..4599492f 100644 --- a/config/initializers/mongoid.rb +++ b/config/initializers/mongoid.rb @@ -1,11 +1,5 @@ require 'mongoid' -File.open(File.join(Rails.root, 'config/database.yml'), 'r') do |f| - @settings = YAML.load(f)[Rails.env] -end - -Mongoid::Config.instance.from_hash(@settings) - ## various patches module Mongoid #:nodoc: diff --git a/config/locales/admin_ui_en.yml b/config/locales/admin_ui_en.yml index ba956b39..79cf7f27 100644 --- a/config/locales/admin_ui_en.yml +++ b/config/locales/admin_ui_en.yml @@ -33,9 +33,12 @@ en: update: Update custom_fields: + edit: + title: Editing custom field kind: string: Simple Input text: Text + select: Select sessions: new: @@ -172,8 +175,7 @@ en: title: '{{type}} — new item' edit: title: '{{type}} — editing item' - - + formtastic: titles: information: General information @@ -196,6 +198,10 @@ en: source: File edit: source: Replace file + custom_fields: + custom_field: + _alias: Alias + hints: page: keywords: "Meta keywords used within the head tag of the page. They are separeted by an empty space. Required for SEO." diff --git a/config/mongoid.yml b/config/mongoid.yml new file mode 100644 index 00000000..c4c29b91 --- /dev/null +++ b/config/mongoid.yml @@ -0,0 +1,24 @@ +defaults: &defaults + host: localhost + # slaves: + # - host: slave1.local + # port: 27018 + # - host: slave2.local + # port: 27019 + +development: + <<: *defaults + database: locomotive_dev + +test: + <<: *defaults + database: locomotive_test + +# set these environment variables on your prod server +production: + <<: *defaults + host: <%= ENV['MONGOID_HOST'] %> + port: <%= ENV['MONGOID_PORT'] %> + username: <%= ENV['MONGOID_USERNAME'] %> + password: <%= ENV['MONGOID_PASSWORD'] %> + database: <%= ENV['MONGOID_DATABASE'] %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 7473ada5..7b57b7fe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -49,6 +49,7 @@ Rails.application.routes.draw do |map| resources :contents, :path => "content_types/:slug/contents" do put :sort, :on => :collection end + end # magic urls diff --git a/doc/TODO b/doc/TODO index 21c5844f..3230b44a 100644 --- a/doc/TODO +++ b/doc/TODO @@ -6,13 +6,10 @@ x make an engine: - deploy on Heroku -- refactoring: CustomFields::CustomField => CustomFields::Field -- new types for custom field - - file - - boolean - - date -- optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item - +x refactoring: CustomFields::CustomField => CustomFields::Field +- new custom field type + - category +x optimization custom_fields: use dynamic class for a collection instead of modifying the metaclass each time we build an item BACKLOG: @@ -24,9 +21,16 @@ BACKLOG: - theme assets: disable version if not image +- new custom field types + - file + - boolean + - date + +- refactor slugify method (use parameterize + create a module) + BUGS: - when assigning new layout, disabled parts show up :-( (js problem) -- password resets +- password resets (url is not handled correctly) NICE TO HAVE: - asset collections: custom resizing if image diff --git a/lib/misc_form_builder.rb b/lib/misc_form_builder.rb index a11e456a..c0a4fb14 100644 --- a/lib/misc_form_builder.rb +++ b/lib/misc_form_builder.rb @@ -32,4 +32,13 @@ class MiscFormBuilder < Formtastic::SemanticFormBuilder end end + def normalize_model_name(name) + if name =~ /(.+)\/(.+)/ + [$1, $2] + else + super + end + end + + end diff --git a/public/images/admin/form/footer-small.png b/public/images/admin/form/footer-small.png new file mode 100644 index 00000000..ae759629 Binary files /dev/null and b/public/images/admin/form/footer-small.png differ diff --git a/public/images/admin/form/header-small.png b/public/images/admin/form/header-small.png new file mode 100644 index 00000000..84719ea5 Binary files /dev/null and b/public/images/admin/form/header-small.png differ diff --git a/public/images/admin/form/icons/edit.png b/public/images/admin/form/icons/edit.png new file mode 100644 index 00000000..49d20c43 Binary files /dev/null and b/public/images/admin/form/icons/edit.png differ diff --git a/public/images/admin/list/none-small.png b/public/images/admin/list/none-small.png new file mode 100644 index 00000000..1ca0172a Binary files /dev/null and b/public/images/admin/list/none-small.png differ diff --git a/public/javascripts/admin/application.js b/public/javascripts/admin/application.js index 08bee1cd..ff8a0549 100644 --- a/public/javascripts/admin/application.js +++ b/public/javascripts/admin/application.js @@ -19,15 +19,16 @@ $.growl.settings.dockCss = { /* ___ codemirror ___ */ var addCodeMirrorEditor = function(type, el, parser) { + if (type == 'liquid') type = 'xml'; var parserfile = "parse" + type + ".js"; - if (parser != undefined) parserfile = parser; - // if (type == 'liquid') type = 'xml'; + if (parser != undefined) parserfile = parser; var editor = CodeMirror.fromTextArea(el.attr('id'), { height: "400px", parserfile: parserfile, stylesheet: [ "/stylesheets/admin/plugins/codemirror/csscolors.css", + "/stylesheets/admin/plugins/codemirror/xmlcolors.css", "/stylesheets/admin/plugins/codemirror/javascriptcolors.css", "/stylesheets/admin/plugins/codemirror/liquidcolors.css"], path: "/javascripts/admin/plugins/codemirror/", diff --git a/public/javascripts/admin/custom_fields.js b/public/javascripts/admin/custom_fields.js index 3d7507bb..800a1d29 100644 --- a/public/javascripts/admin/custom_fields.js +++ b/public/javascripts/admin/custom_fields.js @@ -110,4 +110,25 @@ $(document).ready(function() { axis: 'y', update: refreshPosition }); + + // edit in depth custom field + $('fieldset.fields li.item span.actions a.edit').click(function() { + var link = $(this); + $.fancybox({ + titleShow: false, + content: $(link.attr('href')).parent().html(), + onComplete: function() { + $('#fancybox-wrap form').submit(function(e) { + $.fancybox.close(); + e.preventDefault(); + e.stopPropagation(); + }); + + $('#fancybox-wrap #custom_fields_custom_field__alias').val(link.parent().prevAll('.alias').val()); + }, + onCleanup: function() { + link.parent().prevAll('.alias').val($('#fancybox-wrap #custom_fields_custom_field__alias').val()); + } + }) + }); }); \ No newline at end of file diff --git a/public/javascripts/admin/snippets.js b/public/javascripts/admin/snippets.js index c66f8077..e4a3ae64 100644 --- a/public/javascripts/admin/snippets.js +++ b/public/javascripts/admin/snippets.js @@ -7,7 +7,7 @@ $(document).ready(function() { if (!slug.hasClass('filled')) { setTimeout(function() { - slug.val(input.val().replace(/\s/g, '_').toLowerCase()); + slug.val(input.val().replace(/[\s']/g, '_').toLowerCase()); }, 50); } }); diff --git a/public/javascripts/admin/theme_assets.js b/public/javascripts/admin/theme_assets.js index efb37e70..da8a79bc 100644 --- a/public/javascripts/admin/theme_assets.js +++ b/public/javascripts/admin/theme_assets.js @@ -71,6 +71,8 @@ var setupUploader = function() { asset.removeClass('new-asset'); + $('.asset-picker p.no-items').hide(); + $('.asset-picker ul').scrollTo($('li.asset:last'), 400); } }); diff --git a/public/stylesheets/admin/box.css b/public/stylesheets/admin/box.css index b694e54a..faf49730 100644 --- a/public/stylesheets/admin/box.css +++ b/public/stylesheets/admin/box.css @@ -1,8 +1,10 @@ /* custom styles for fancybox */ -div.asset-picker { width: 470px; position: relative; } -div.asset-picker .actions { position: absolute; right: 4px; top: 0px; } -div.asset-picker h2 { +/* ___ common ___ */ + +.box-wrapper { display: none; } + +#fancybox-inner h2 { border-bottom:1px dotted #BBBBBD; color:#1E1F26; font-size:1.1em; @@ -10,5 +12,28 @@ div.asset-picker h2 { padding-bottom:10px; } +#fancybox-inner form.formtastic legend span { + background-image: url("/images/admin/form/header-small.png"); + width: 450px; +} + +#fancybox-inner form.formtastic ol { + background-image: url("/images/admin/form/footer-small.png"); +} + + +/* ___ asset picker ___ */ + +div.asset-picker { width: 470px; position: relative; } +div.asset-picker .actions { position: absolute; right: 4px; top: 0px; } + +div.asset-picker p.no-items { background-image: url("/images/admin/list/none-small.png"); } + div.asset-picker ul { overflow: auto; height: 471px; } -div.asset-picker ul li.new-asset { display: none; } \ No newline at end of file +div.asset-picker ul li.new-asset { display: none; } + +/* ___ custom fields ___ */ + +#edit-custom-field { + width: 470px; +} \ No newline at end of file diff --git a/public/stylesheets/admin/formtastic_changes.css b/public/stylesheets/admin/formtastic_changes.css index 443c7407..7a64101c 100644 --- a/public/stylesheets/admin/formtastic_changes.css +++ b/public/stylesheets/admin/formtastic_changes.css @@ -226,8 +226,9 @@ form.formtastic fieldset ol li.item span.actions { position: absolute; top: 7px; right: 10px; - width: 16px; + width: 50px; height: 16px; + text-align:right; } /* ___ editable-list (content type fields and validations) ___ */ @@ -338,6 +339,10 @@ form.formtastic fieldset.editable-list ol li.template span.actions { top: 10px; } +form.formtastic fieldset.editable-list ol li.template span.actions a.edit { + display: none; +} + form.formtastic fieldset.editable-list ol li.template span.actions a.remove { display: none; } diff --git a/spec/models/asset_collections_spec.rb b/spec/models/asset_collections_spec.rb index 8918813e..638ec07a 100644 --- a/spec/models/asset_collections_spec.rb +++ b/spec/models/asset_collections_spec.rb @@ -20,7 +20,7 @@ describe AssetCollection do context 'unit' do before(:each) do - @field = CustomFields::CustomField.new(:kind => 'String') + @field = CustomFields::Field.new(:kind => 'String') end it 'should tell if it is a String' do @@ -75,6 +75,7 @@ describe AssetCollection do context 'build and save' do it 'should build asset' do + puts "___ TEST #1 ___" asset = @collection.assets.build lambda { asset.description @@ -84,12 +85,14 @@ describe AssetCollection do end it 'should assign values to newly built asset' do + puts "___ TEST #2 ___" asset = build_asset(@collection) asset.description.should == 'Lorem ipsum' asset.active.should == true end it 'should save asset' do + puts "___ TEST #3 ___" asset = build_asset(@collection) asset.save and @collection.reload asset = @collection.assets.first @@ -98,6 +101,7 @@ describe AssetCollection do end it 'should not modify assets from another collection' do + puts "___ TEST #4 ___" asset = build_asset(@collection) asset.save and @collection.reload new_collection = AssetCollection.new @@ -164,7 +168,7 @@ describe AssetCollection do end end - + end def build_asset(collection) diff --git a/vendor/plugins/custom_fields/.rspec b/vendor/plugins/custom_fields/.rspec new file mode 100644 index 00000000..f9b2fb78 --- /dev/null +++ b/vendor/plugins/custom_fields/.rspec @@ -0,0 +1,3 @@ +--colour +--format nested +--backtrace \ No newline at end of file diff --git a/vendor/plugins/custom_fields/Gemfile b/vendor/plugins/custom_fields/Gemfile new file mode 100644 index 00000000..a0698497 --- /dev/null +++ b/vendor/plugins/custom_fields/Gemfile @@ -0,0 +1,11 @@ +source "http://gemcutter.org" + +gem "bson_ext", ">= 1.0.1" +gem "mongo_ext" +gem "mongoid", ">= 2.0.0.beta6" +gem "activesupport", ">= 3.0.0.beta3" + +group :test do + gem 'rspec', '>= 2.0.0.beta.10' + gem 'mocha', :git => 'git://github.com/floehopper/mocha.git' +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/README b/vendor/plugins/custom_fields/README index 99cd98b0..25e32db2 100644 --- a/vendor/plugins/custom_fields/README +++ b/vendor/plugins/custom_fields/README @@ -1,4 +1,4 @@ -CustomField +CustomFields =========== Introduction goes here. @@ -10,4 +10,4 @@ Example Example goes here. -Copyright (c) 2010 [name of plugin creator], released under the MIT license +Copyright (c) 2010 [Didier Lafforgue], released under the MIT license diff --git a/vendor/plugins/custom_fields/Rakefile b/vendor/plugins/custom_fields/Rakefile index 6276c122..b48cae71 100644 --- a/vendor/plugins/custom_fields/Rakefile +++ b/vendor/plugins/custom_fields/Rakefile @@ -1,23 +1,33 @@ -require 'rake' -require 'rake/testtask' -require 'rake/rdoctask' +require "rubygems" +require "rake" +require "rake/rdoctask" +require "rspec" +require "rspec/core/rake_task" -desc 'Default: run unit tests.' -task :default => :test - -desc 'Test the custom_field plugin.' -Rake::TestTask.new(:test) do |t| - t.libs << 'lib' - t.libs << 'test' - t.pattern = 'test/**/*_test.rb' - t.verbose = true -end - -desc 'Generate documentation for the custom_field plugin.' +desc 'Generate documentation for the custom_fields plugin.' Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'rdoc' - rdoc.title = 'CustomField' + rdoc.title = 'CustomFields' rdoc.options << '--line-numbers' << '--inline-source' rdoc.rdoc_files.include('README') rdoc.rdoc_files.include('lib/**/*.rb') -end \ No newline at end of file +end + +# Spec::Rake::SpecTask.new(:rcov) do |spec| +# spec.libs << 'lib' << 'spec' +# spec.pattern = 'spec/**/*_spec.rb' +# spec.rcov = true +# end + +Rspec::Core::RakeTask.new('spec:unit') do |spec| + spec.pattern = "spec/unit/**/*_spec.rb" + # spec.pattern = "spec/unit/custom_fields_for_spec.rb" +end + +Rspec::Core::RakeTask.new('spec:integration') do |spec| + spec.pattern = "spec/integration/**/*_spec.rb" +end + +task :spec => [:check_dependencies, 'spec:unit', 'spec:integration'] + +task :default => :spec \ No newline at end of file diff --git a/vendor/plugins/custom_fields/install.rb b/vendor/plugins/custom_fields/install.rb deleted file mode 100644 index f7732d37..00000000 --- a/vendor/plugins/custom_fields/install.rb +++ /dev/null @@ -1 +0,0 @@ -# Install hook code here diff --git a/vendor/plugins/custom_fields/lib/custom_fields.rb b/vendor/plugins/custom_fields/lib/custom_fields.rb index 25298d55..7c11c01f 100644 --- a/vendor/plugins/custom_fields/lib/custom_fields.rb +++ b/vendor/plugins/custom_fields/lib/custom_fields.rb @@ -1,7 +1,15 @@ $:.unshift File.expand_path(File.dirname(__FILE__)) +require 'active_support' + +require 'custom_fields/extensions/mongoid/associations/proxy' +require 'custom_fields/extensions/mongoid/associations/has_many_related' require 'custom_fields/extensions/mongoid/associations/embeds_many' require 'custom_fields/extensions/mongoid/document' +require 'custom_fields/types/default' +require 'custom_fields/types/category' +require 'custom_fields/proxy_class_enabler' +require 'custom_fields/field' require 'custom_fields/custom_fields_for' module Mongoid diff --git a/vendor/plugins/custom_fields/lib/custom_fields/custom_fields_for.rb b/vendor/plugins/custom_fields/lib/custom_fields/custom_fields_for.rb index f1d3277f..1d9e9fbb 100644 --- a/vendor/plugins/custom_fields/lib/custom_fields/custom_fields_for.rb +++ b/vendor/plugins/custom_fields/lib/custom_fields/custom_fields_for.rb @@ -30,7 +30,7 @@ module CustomFields class_eval <<-EOV field :#{singular_name}_custom_fields_counter, :type => Integer, :default => 0 - embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::CustomField" + embeds_many :#{singular_name}_custom_fields, :class_name => "::CustomFields::Field" validates_associated :#{singular_name}_custom_fields @@ -39,6 +39,7 @@ module CustomFields def ordered_#{singular_name}_custom_fields self.#{singular_name}_custom_fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) } end + EOV end diff --git a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/embeds_many.rb b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/embeds_many.rb index a419d36a..4375485c 100644 --- a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/embeds_many.rb +++ b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/embeds_many.rb @@ -2,6 +2,27 @@ module Mongoid #:nodoc: module Associations #:nodoc: class EmbedsMany < Proxy + def initialize_with_custom_fields(parent, options, target_array = nil) + if custom_fields?(parent, options.name) + options = options.clone # 2 parent instances should not share the exact same option instance + + custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}") + + klass = options.klass.to_klass_with_custom_fields(custom_fields) + + options.instance_eval <<-EOF + def klass=(klass); @klass = klass; end + def klass; @klass || class_name.constantize; end + EOF + + options.klass = klass + end + + initialize_without_custom_fields(parent, options, target_array) + end + + alias_method_chain :initialize, :custom_fields + def build_with_custom_field_settings(attrs = {}, type = nil) document = build_without_custom_field_settings(attrs, type) @@ -19,6 +40,5 @@ module Mongoid #:nodoc: alias_method_chain :build, :custom_field_settings end - end end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/has_many_related.rb b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/has_many_related.rb new file mode 100644 index 00000000..a94d1107 --- /dev/null +++ b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/has_many_related.rb @@ -0,0 +1,31 @@ +# encoding: utf-8 +module Mongoid #:nodoc: + module Associations #:nodoc: + # Represents an relational one-to-many association with an object in a + # separate collection or database. + class HasManyRelated < Proxy + + def initialize_with_custom_fields(parent, options, target_array = nil) + if custom_fields?(parent, options.name) + options = options.clone # 2 parent instances should not share the exact same option instance + + custom_fields = parent.send(:"ordered_#{custom_fields_association_name(options.name)}") + + klass = options.klass.to_klass_with_custom_fields(custom_fields) + + options.instance_eval <<-EOF + def klass=(klass); @klass = klass; end + def klass; @klass || class_name.constantize; end + EOF + + options.klass = klass + end + + initialize_without_custom_fields(parent, options, target_array) + end + + alias_method_chain :initialize, :custom_fields + + end + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/proxy.rb b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/proxy.rb new file mode 100644 index 00000000..7a3fd8d4 --- /dev/null +++ b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/associations/proxy.rb @@ -0,0 +1,16 @@ +# encoding: utf-8 +module Mongoid #:nodoc + module Associations #:nodoc + class Proxy #:nodoc + + def custom_fields_association_name(association_name) + "#{association_name.to_s.singularize}_custom_fields".to_sym + end + + def custom_fields?(object, association_name) + object.respond_to?(custom_fields_association_name(association_name)) + end + + end + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/document.rb b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/document.rb index 4e7d888b..45f994a8 100644 --- a/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/document.rb +++ b/vendor/plugins/custom_fields/lib/custom_fields/extensions/mongoid/document.rb @@ -2,34 +2,34 @@ module Mongoid #:nodoc: module Document module InstanceMethods - def parentize_with_custom_fields(object, association_name) - parentize_without_custom_fields(object, association_name) - - if self.custom_fields?(object, association_name) - # puts "[parentize_with_custom_fields] association_name = #{association_name} / #{self.custom_fields_association_name(association_name)}" - object.send(self.custom_fields_association_name(association_name)).each do |field| - field.apply(self, association_name) - end - - self.instance_eval <<-EOV - def custom_fields - fields = self._parent.send(:#{self.custom_fields_association_name(association_name)}) - fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) } - end - EOV - end - end - - alias_method_chain :parentize, :custom_fields - - def custom_fields_association_name(association_name) - "#{association_name.to_s.singularize}_custom_fields".to_sym - end - - def custom_fields?(object, association_name) - object.respond_to?(custom_fields_association_name(association_name)) && - object.associations[association_name] - end + # def parentize_with_custom_fields(object, association_name) + # parentize_without_custom_fields(object, association_name) + # + # if self.custom_fields?(object, association_name) + # # puts "[parentize_with_custom_fields] association_name = #{association_name} / #{self.custom_fields_association_name(association_name)}" + # object.send(self.custom_fields_association_name(association_name)).each do |field| + # field.apply(self) + # end + # + # self.instance_eval <<-EOV + # def custom_fields + # fields = self._parent.send(:#{self.custom_fields_association_name(association_name)}) + # fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) } + # end + # EOV + # end + # end + # + # alias_method_chain :parentize, :custom_fields + # + # def custom_fields_association_name(association_name) + # "#{association_name.to_s.singularize}_custom_fields".to_sym + # end + # + # def custom_fields?(object, association_name) + # object.respond_to?(custom_fields_association_name(association_name)) && + # object.associations[association_name] + # end end end end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/custom_field.rb b/vendor/plugins/custom_fields/lib/custom_fields/field.rb similarity index 56% rename from vendor/plugins/custom_fields/lib/custom_fields/custom_field.rb rename to vendor/plugins/custom_fields/lib/custom_fields/field.rb index 7e60b11f..ea3f1c5b 100644 --- a/vendor/plugins/custom_fields/lib/custom_fields/custom_field.rb +++ b/vendor/plugins/custom_fields/lib/custom_fields/field.rb @@ -1,8 +1,12 @@ module CustomFields - class CustomField - include Mongoid::Document - include Mongoid::Timestamps + class Field + include ::Mongoid::Document + include ::Mongoid::Timestamps + + # types ## + include Types::Default + include Types::Category ## fields ## field :label, :type => String @@ -17,7 +21,7 @@ module CustomFields ## methods ## - %w{String Text Email Boolean Date File}.each do |kind| + %w{String Text Category}.each do |kind| define_method "#{kind.downcase}?" do self.kind == kind end @@ -25,27 +29,42 @@ module CustomFields def field_type case self.kind - when 'String', 'Text', 'Email' then String + when 'String', 'Text', 'Category' then String else self.kind.constantize end end - - def apply(object, association_name) + + def apply(klass) return unless self.valid? - # trick mongoid - object.class_eval { def meta; (class << self; self; end); end } - object.meta.fields = object.fields.clone - object.meta.send(:define_method, :fields) { self.meta.fields } + klass.field self._name, :type => self.field_type - object.meta.field self._name, :type => self.field_type - object.class_eval <<-EOF - alias :#{self.safe_alias} :#{self._name} - alias :#{self.safe_alias}= :#{self._name}= - EOF + case self.kind + when 'Category' + apply_category_type(klass) + else + apply_default_type(klass) + end end + # def apply_to_object(object) + # return unless self.valid? + # + # # trick mongoid: fields are now on a the singleton class level also called metaclass + # self.singleton_class.fields = self.fields.clone + # self.singleton_class.send(:define_method, :fields) { self.singleton_class.fields } + # + # object.singleton_class.field self._name, :type => self.field_type + # + # case self.kind + # when 'Category' + # apply_category_type(object) + # else + # apply_default_type(object) + # end + # end + def safe_alias self.set_alias self._alias @@ -66,8 +85,7 @@ module CustomFields def set_alias return if self.label.blank? && self._alias.blank? - self._alias ||= self.label.clone - self._alias.slugify!(:downcase => true, :underscore => true) + self._alias = (self._alias || self.label).parameterize('_').downcase end def increment_counter! diff --git a/vendor/plugins/custom_fields/lib/custom_fields/proxy_class_enabler.rb b/vendor/plugins/custom_fields/lib/custom_fields/proxy_class_enabler.rb new file mode 100644 index 00000000..3cfce005 --- /dev/null +++ b/vendor/plugins/custom_fields/lib/custom_fields/proxy_class_enabler.rb @@ -0,0 +1,37 @@ +module CustomFields + module ProxyClassEnabler + + extend ActiveSupport::Concern + + included do + + cattr_accessor :klass_with_custom_fields + + def self.to_klass_with_custom_fields(fields) + return klass_with_custom_fields unless klass_with_custom_fields.nil? + + klass = Class.new(self) + klass.class_eval <<-EOF + cattr_accessor :custom_fields + + def self.model_name + @_model_name ||= ActiveModel::Name.new(self.superclass) + end + + def custom_fields + self.class.custom_fields + end + EOF + + klass.hereditary = false + klass.custom_fields = fields + + [*fields].each { |field| field.apply(klass) } + + klass_with_custom_fields = klass + end + + end + + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/types/category.rb b/vendor/plugins/custom_fields/lib/custom_fields/types/category.rb new file mode 100644 index 00000000..282756cf --- /dev/null +++ b/vendor/plugins/custom_fields/lib/custom_fields/types/category.rb @@ -0,0 +1,52 @@ +module CustomFields + module Types + module Category + + extend ActiveSupport::Concern + + included do + embeds_many :category_items, :class_name => 'CustomFields::Types::Category::Item' + end + + module InstanceMethods + + def ordered_category_items + self.category_items.sort { |a, b| (a.position || 0) <=> (b.position || 0) } + end + + def apply_category_type(klass) + klass.cattr_accessor :"#{self.safe_alias}_items" + + klass.send("#{self.safe_alias}_items=", self.category_items) + + klass.class_eval <<-EOF + def self.#{self.safe_alias}_names + #{self.safe_alias}_items.collect(&:name) + end + + def #{self.safe_alias}=(name) + category_id = self.class.#{self.safe_alias}_items.where(:name => name).first._id rescue nil + write_attribute(:#{self._name}, category_id) + end + + def #{self.safe_alias} + category_id = read_attribute(:#{self._name}) + self.class.#{self.safe_alias}_items.find(category_id).name rescue nil + end + EOF + end + + end + + class Item + + include Mongoid::Document + + field :name + field :position, :type => Integer, :default => 0 + + embedded_in :custom_field, :inverse_of => :category_items + end + end + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/lib/custom_fields/types/default.rb b/vendor/plugins/custom_fields/lib/custom_fields/types/default.rb new file mode 100644 index 00000000..41b5aff6 --- /dev/null +++ b/vendor/plugins/custom_fields/lib/custom_fields/types/default.rb @@ -0,0 +1,18 @@ +module CustomFields + module Types + module Default + extend ActiveSupport::Concern + + module InstanceMethods + + def apply_default_type(klass) + klass.class_eval <<-EOF + alias :#{self.safe_alias} :#{self._name} + alias :#{self.safe_alias}= :#{self._name}= + EOF + end + + end + end + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/models/person.rb b/vendor/plugins/custom_fields/spec/models/person.rb new file mode 100644 index 00000000..47ed257a --- /dev/null +++ b/vendor/plugins/custom_fields/spec/models/person.rb @@ -0,0 +1,10 @@ +class Person + + include Mongoid::Document + include CustomFields::ProxyClassEnabler + + field :full_name + + embedded_in :project, :inverse_of => :people + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/models/project.rb b/vendor/plugins/custom_fields/spec/models/project.rb new file mode 100644 index 00000000..bea9c09f --- /dev/null +++ b/vendor/plugins/custom_fields/spec/models/project.rb @@ -0,0 +1,16 @@ +class Project + + include Mongoid::Document + include CustomFields::ProxyClassEnabler + include CustomFields::CustomFieldsFor + + field :name + field :description + + has_many_related :people + embeds_many :tasks + + custom_fields_for :people + custom_fields_for :tasks + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/models/task.rb b/vendor/plugins/custom_fields/spec/models/task.rb new file mode 100644 index 00000000..36cba66e --- /dev/null +++ b/vendor/plugins/custom_fields/spec/models/task.rb @@ -0,0 +1,10 @@ +class Task + + include Mongoid::Document + include CustomFields::ProxyClassEnabler + + field :title + + embedded_in :project, :inverse_of => :tasks + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/spec_helper.rb b/vendor/plugins/custom_fields/spec/spec_helper.rb new file mode 100644 index 00000000..861624f4 --- /dev/null +++ b/vendor/plugins/custom_fields/spec/spec_helper.rb @@ -0,0 +1,30 @@ +$LOAD_PATH.unshift(File.dirname(__FILE__)) +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib")) + +MODELS = File.join(File.dirname(__FILE__), "models") +$LOAD_PATH.unshift(MODELS) + +require 'rubygems' +require 'bundler' +Bundler.setup +Bundler.require + +require 'mongoid' +require 'mocha' +require 'rspec' +require 'custom_fields' + +Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) } + +Mongoid.configure do |config| + name = "custom_fields_test" + host = "localhost" + config.master = Mongo::Connection.new.db(name) +end + +Rspec.configure do |config| + config.mock_with :mocha + config.after :suite do + Mongoid.master.collections.each(&:drop) + end +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/unit/custom_field_spec.rb b/vendor/plugins/custom_fields/spec/unit/custom_field_spec.rb new file mode 100644 index 00000000..3799dd9f --- /dev/null +++ b/vendor/plugins/custom_fields/spec/unit/custom_field_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe CustomFields::Field do + + it 'is initialized' do + lambda { CustomFields::Field.new }.should_not raise_error + end + + context '#mongoid' do + + before(:each) do + @field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager') + @field.stubs(:valid?).returns(true) + @project = Project.to_klass_with_custom_fields(@field).new + end + + it 'is added to the list of mongoid fields' do + @project.fields['field_1'].should_not be_nil + end + + end + + context 'on target class' do + + before(:each) do + @field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager') + @field.stubs(:valid?).returns(true) + @project = Project.to_klass_with_custom_fields(@field).new + end + + it 'has a new field' do + @project.respond_to?(:manager).should be_true + end + + it 'sets / retrieves a value' do + @project.manager = 'Mickael Scott' + @project.manager.should == 'Mickael Scott' + end + + end + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/unit/custom_fields_for_spec.rb b/vendor/plugins/custom_fields/spec/unit/custom_fields_for_spec.rb new file mode 100644 index 00000000..590864d4 --- /dev/null +++ b/vendor/plugins/custom_fields/spec/unit/custom_fields_for_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe CustomFields::CustomFieldsFor do + + context 'with embedded collection' do + + context '#association' do + + before(:each) do + @project = Project.new + end + + it 'has custom fields for embedded collection' do + @project.respond_to?(:task_custom_fields).should be_true + end + + end + + context '#building' do + + before(:each) do + @project = Project.new + @project.task_custom_fields.build :label => 'Short summary', :_alias => 'summary', :kind => 'String' + @task = @project.tasks.build + end + + it 'returns a new document whose Class is different from the original one' do + @task.class.should_not == Task + end + + it 'returns a new document with custom field' do + @project.tasks.build + @project.tasks.build + @task.respond_to?(:summary).should be_true + end + + it 'sets/gets custom attributes' do + @task.summary = 'Lorem ipsum...' + @task.summary.should == 'Lorem ipsum...' + end + + end + + end + + context 'with related collection' do + + context '#association' do + + before(:each) do + @project = Project.new + end + + it 'has custom fields for related collections' do + @project.respond_to?(:person_custom_fields).should be_true + end + + end + + context '#building' do + + before(:each) do + @project = Project.new + @project.person_custom_fields.build :label => 'Position in the project', :_alias => 'position', :kind => 'String' + @person = @project.people.build + end + + it 'returns a new document whose Class is different from the original one' do + @person.class.should_not == Task + end + + it 'returns a new document with custom field' do + @person.respond_to?(:position).should be_true + end + + it 'sets/gets custom attributes' do + @person.position = 'Designer' + @person.position.should == 'Designer' + end + + end + + end + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/unit/proxy_class_enabler_spec.rb b/vendor/plugins/custom_fields/spec/unit/proxy_class_enabler_spec.rb new file mode 100644 index 00000000..ec7d5029 --- /dev/null +++ b/vendor/plugins/custom_fields/spec/unit/proxy_class_enabler_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe CustomFields::ProxyClassEnabler do + + context '#proxy klass' do + + before(:each) do + @klass = Task.to_klass_with_custom_fields([]) + end + + it 'does not be flagged as a inherited document' do + @klass.new.hereditary?.should be_false + end + + it 'has a list of custom fields' do + @klass.custom_fields.should == [] + end + + it 'has the exact same model name than its parent' do + @klass.model_name.should == 'Task' + end + + end + +end \ No newline at end of file diff --git a/vendor/plugins/custom_fields/spec/unit/types/category_spec.rb b/vendor/plugins/custom_fields/spec/unit/types/category_spec.rb new file mode 100644 index 00000000..0e1f307e --- /dev/null +++ b/vendor/plugins/custom_fields/spec/unit/types/category_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe CustomFields::Types::Category do + + context 'on field class' do + + before(:each) do + @field = CustomFields::Field.new + end + + it 'has the category items field' do + @field.respond_to?(:category_items).should be_true + end + + it 'has the apply method used for the target object' do + @field.respond_to?(:apply_category_type).should be_true + end + + end + + context 'on target class' do + + before(:each) do + @project = build_project_with_category + end + + it 'has getter/setter' do + @project.respond_to?(:global_category).should be_true + @project.respond_to?(:global_category=).should be_true + end + + it 'has the values of this category' do + @project.class.global_category_names.should == %w{Development Design Maintenance} + end + + it 'sets the category from a name' do + @project.global_category = 'Design' + @project.global_category.should == 'Design' + @project.field_1.should_not be_nil + end + + end + + def build_project_with_category + field = build_category + Project.to_klass_with_custom_fields(field).new + end + + def build_category + field = CustomFields::Field.new(:label => 'global_category', :_name => 'field_1', :kind => 'Category') + field.stubs(:valid?).returns(true) + field.category_items.build :name => 'Development' + field.category_items.build :name => 'Design' + field.category_items.build :name => 'Maintenance' + field + end + +end \ No newline at end of file