custom_fields 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README +18 -0
  2. data/init.rb +2 -0
  3. data/lib/custom_fields.rb +28 -0
  4. data/lib/custom_fields/custom_fields_for.rb +50 -0
  5. data/lib/custom_fields/extensions/mongoid/associations/embeds_many.rb +31 -0
  6. data/lib/custom_fields/extensions/mongoid/associations/proxy.rb +20 -0
  7. data/lib/custom_fields/extensions/mongoid/associations/references_many.rb +33 -0
  8. data/lib/custom_fields/extensions/mongoid/hierarchy.rb +28 -0
  9. data/lib/custom_fields/field.rb +82 -0
  10. data/lib/custom_fields/proxy_class_enabler.rb +40 -0
  11. data/lib/custom_fields/types/boolean.rb +29 -0
  12. data/lib/custom_fields/types/category.rb +88 -0
  13. data/lib/custom_fields/types/date.rb +35 -0
  14. data/lib/custom_fields/types/default.rb +37 -0
  15. data/lib/custom_fields/types/file.rb +27 -0
  16. data/lib/custom_fields/types/string.rb +13 -0
  17. data/lib/custom_fields/types/text.rb +15 -0
  18. data/spec/integration/custom_fields_for_spec.rb +28 -0
  19. data/spec/integration/types/category_spec.rb +48 -0
  20. data/spec/integration/types/file_spec.rb +18 -0
  21. data/spec/models/person.rb +10 -0
  22. data/spec/models/project.rb +18 -0
  23. data/spec/models/task.rb +10 -0
  24. data/spec/spec_helper.rb +27 -0
  25. data/spec/support/carrierwave.rb +31 -0
  26. data/spec/support/mongoid.rb +6 -0
  27. data/spec/unit/custom_field_spec.rb +42 -0
  28. data/spec/unit/custom_fields_for_spec.rb +106 -0
  29. data/spec/unit/proxy_class_enabler_spec.rb +25 -0
  30. data/spec/unit/types/boolean_spec.rb +81 -0
  31. data/spec/unit/types/category_spec.rb +116 -0
  32. data/spec/unit/types/date_spec.rb +59 -0
  33. data/spec/unit/types/file_spec.rb +23 -0
  34. metadata +116 -0
@@ -0,0 +1,35 @@
1
+ module CustomFields
2
+ module Types
3
+ module Date
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ register_type :date, ::Date
9
+ end
10
+
11
+ module InstanceMethods
12
+
13
+ def apply_date_type(klass)
14
+
15
+ klass.class_eval <<-EOF
16
+ def #{self.safe_alias}
17
+ self.#{self._name}.strftime(I18n.t('date.formats.default')) rescue nil
18
+ end
19
+
20
+ def #{self.safe_alias}=(value)
21
+ if value.is_a?(String)
22
+ date = ::Date._strptime(value, I18n.t('date.formats.default'))
23
+ value = Date.new(date[:year], date[:mon], date[:mday])
24
+ end
25
+ self.#{self._name} = value
26
+ end
27
+ EOF
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ module CustomFields
2
+ module Types
3
+ module Default
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ cattr_accessor :field_types
8
+ end
9
+
10
+ module InstanceMethods
11
+
12
+ def apply_default_type(klass)
13
+ klass.class_eval <<-EOF
14
+ alias :#{self.safe_alias} :#{self._name}
15
+ alias :#{self.safe_alias}= :#{self._name}=
16
+ EOF
17
+ end
18
+
19
+ end
20
+
21
+ module ClassMethods
22
+
23
+ def register_type(kind, klass = ::String)
24
+ self.field_types ||= {}
25
+ self.field_types[kind.to_sym] = klass unless klass.nil?
26
+
27
+ self.class_eval <<-EOF
28
+ def #{kind.to_s}?
29
+ self.kind.downcase == '#{kind}' rescue false
30
+ end
31
+ EOF
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ module CustomFields
2
+ module Types
3
+ module File
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ register_type :file, nil # do not create the default field
9
+ end
10
+
11
+ module InstanceMethods
12
+
13
+ def apply_file_type(klass)
14
+
15
+ klass.mount_uploader self._name, FileUploader
16
+
17
+ self.apply_default_type(klass)
18
+ end
19
+
20
+ end
21
+
22
+ class FileUploader < ::CarrierWave::Uploader::Base
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module CustomFields
2
+ module Types
3
+ module String
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ register_type :string
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module CustomFields
2
+ module Types
3
+ module Text
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ field :text_formatting, :default => 'html'
9
+
10
+ register_type :text
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomFields::CustomFieldsFor do
4
+
5
+ describe 'Saving' do
6
+
7
+ before(:each) do
8
+ @project = Project.new(:name => 'Locomotive')
9
+ @project.person_custom_fields.build(:label => 'E-mail', :_alias => 'email', :kind => 'String')
10
+ @project.person_custom_fields.build(:label => 'Age', :_alias => 'age', :kind => 'String')
11
+ end
12
+
13
+ context '@create' do
14
+
15
+ it 'persists parent object' do
16
+ lambda { @project.save }.should change(Project, :count).by(1)
17
+ end
18
+
19
+ it 'persists custom fields' do
20
+ @project.save && @project.reload
21
+ @project.person_custom_fields.count.should == 2
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomFields::Types::Category do
4
+
5
+ before(:each) do
6
+ @project = Project.new(:name => 'Locomotive')
7
+ @field = @project.task_custom_fields.build(:label => 'Main category', :_alias => 'main_category', :kind => 'Category')
8
+ end
9
+
10
+ context 'saving category items' do
11
+
12
+ before(:each) do
13
+ @field.category_items.build :name => 'Development'
14
+ @field.category_items.build :name => 'Design'
15
+ @field.updated_at = Time.now
16
+ end
17
+
18
+ it 'persists items' do
19
+ @field.save.should be_true
20
+ @project.reload
21
+ @project.task_custom_fields.first.category_items.size.should == 2
22
+ end
23
+
24
+ context 'assigning a category and persists' do
25
+
26
+ it 'sets the category from a category name' do
27
+ task = @project.tasks.build(:main_category => 'Design')
28
+ task.save && @project.reload
29
+ @project.tasks.first.main_category.should == 'Design'
30
+ end
31
+
32
+ it 'sets the category from an id (string)' do
33
+ task = @project.tasks.build(:main_category => @field.category_items.last._id.to_s)
34
+ task.save && @project.reload
35
+ @project.tasks.first.main_category.should == 'Design'
36
+ end
37
+
38
+ it 'sets the category from an id (BSON::ObjectID)' do
39
+ task = @project.tasks.build(:main_category => @field.category_items.last._id)
40
+ task.save && @project.reload
41
+ @project.tasks.first.main_category.should == 'Design'
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomFields::Types::File do
4
+
5
+ before(:each) do
6
+ @project = Project.new(:name => 'Locomotive')
7
+ @project.task_custom_fields.build(:label => 'Screenshot', :_alias => 'screenshot', :kind => 'File')
8
+ @project.save
9
+ @task = @project.tasks.build
10
+ end
11
+
12
+ it 'attaches file' do
13
+ @task.screenshot = FixturedFile.open('doc.txt')
14
+ @task.save
15
+ @task.screenshot.url.should == '/uploads/doc.txt'
16
+ end
17
+
18
+ end
@@ -0,0 +1,10 @@
1
+ class Person
2
+
3
+ include Mongoid::Document
4
+ include CustomFields::ProxyClassEnabler
5
+
6
+ field :full_name
7
+
8
+ embedded_in :project, :inverse_of => :people
9
+
10
+ end
@@ -0,0 +1,18 @@
1
+ class Project
2
+
3
+ include Mongoid::Document
4
+ include CustomFields::ProxyClassEnabler
5
+ include CustomFields::CustomFieldsFor
6
+
7
+ field :name
8
+ field :description
9
+
10
+ references_many :people
11
+ embeds_many :tasks
12
+
13
+ custom_fields_for :people
14
+ custom_fields_for :tasks
15
+
16
+ scope :ordered, :order_by => [[:name, :asc]]
17
+
18
+ end
@@ -0,0 +1,10 @@
1
+ class Task
2
+
3
+ include Mongoid::Document
4
+ include CustomFields::ProxyClassEnabler
5
+
6
+ field :title
7
+
8
+ embedded_in :project, :inverse_of => :tasks
9
+
10
+ end
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ MODELS = File.join(File.dirname(__FILE__), 'models')
5
+ $LOAD_PATH.unshift(MODELS)
6
+
7
+ require 'rubygems'
8
+ require 'bundler'
9
+ Bundler.setup
10
+ Bundler.require
11
+
12
+ require 'mongoid'
13
+ require 'mocha'
14
+ require 'rspec'
15
+ require 'custom_fields'
16
+
17
+ Dir[ File.join(MODELS, "*.rb") ].sort.each { |file| require File.basename(file) }
18
+
19
+ require 'support/mongoid'
20
+ require 'support/carrierwave'
21
+
22
+ Rspec.configure do |config|
23
+ config.mock_with :mocha
24
+ config.after :suite do
25
+ Mongoid.master.collections.select { |c| c.name != 'system.indexes' }.each(&:drop)
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ require 'carrierwave/test/matchers'
2
+
3
+ CarrierWave.configure do |config|
4
+ config.storage = :file
5
+ config.store_dir = "uploads"
6
+ config.cache_dir = "cache"
7
+ config.root = File.join(File.dirname(__FILE__), '..', 'tmp')
8
+ end
9
+
10
+ module FixturedFile
11
+ def self.open(filename)
12
+ File.new(self.path(filename))
13
+ end
14
+
15
+ def self.path(filename)
16
+ File.join(File.dirname(__FILE__), '..', 'fixtures', filename)
17
+ end
18
+
19
+ def self.duplicate(filename)
20
+ dst = File.join(File.dirname(__FILE__), '..', 'tmp', filename)
21
+ FileUtils.cp self.path(filename), dst
22
+ dst
23
+ end
24
+
25
+ def self.reset!
26
+ FileUtils.rm_rf(File.join(File.dirname(__FILE__), '..', 'tmp'))
27
+ FileUtils.mkdir(File.join(File.dirname(__FILE__), '..', 'tmp'))
28
+ end
29
+ end
30
+
31
+ FixturedFile.reset!
@@ -0,0 +1,6 @@
1
+ Mongoid.configure do |config|
2
+ name = "custom_fields_test"
3
+ host = "localhost"
4
+ config.master = Mongo::Connection.new.db(name)
5
+ # config.master = Mongo::Connection.new('localhost', '27017', :logger => Logger.new($stdout)).db(name)
6
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomFields::Field do
4
+
5
+ it 'is initialized' do
6
+ lambda { CustomFields::Field.new }.should_not raise_error
7
+ end
8
+
9
+ context '#mongoid' do
10
+
11
+ before(:each) do
12
+ @field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager')
13
+ @field.stubs(:valid?).returns(true)
14
+ @project = Project.to_klass_with_custom_fields(@field).new
15
+ end
16
+
17
+ it 'is added to the list of mongoid fields' do
18
+ @project.fields['field_1'].should_not be_nil
19
+ end
20
+
21
+ end
22
+
23
+ context 'on target class' do
24
+
25
+ before(:each) do
26
+ @field = CustomFields::Field.new(:label => 'manager', :_name => 'field_1', :kind => 'String', :_alias => 'manager')
27
+ @field.stubs(:valid?).returns(true)
28
+ @project = Project.to_klass_with_custom_fields(@field).new
29
+ end
30
+
31
+ it 'has a new field' do
32
+ @project.respond_to?(:manager).should be_true
33
+ end
34
+
35
+ it 'sets / retrieves a value' do
36
+ @project.manager = 'Mickael Scott'
37
+ @project.manager.should == 'Mickael Scott'
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe CustomFields::CustomFieldsFor do
4
+
5
+ context '#proxy class' do
6
+
7
+ before(:each) do
8
+ @project = Project.new
9
+ @klass = @project.tasks.klass
10
+ end
11
+
12
+ it 'returns the proxy class in the association' do
13
+ @klass.should == @project.tasks.build.class
14
+ end
15
+
16
+ it 'has a link to the parent' do
17
+ @klass._parent.should == @project
18
+ end
19
+
20
+ it 'has the association name which references to' do
21
+ @klass.association_name.should == 'tasks'
22
+ end
23
+
24
+ end
25
+
26
+ context 'with embedded collection' do
27
+
28
+ context '#association' do
29
+
30
+ before(:each) do
31
+ @project = Project.new
32
+ end
33
+
34
+ it 'has custom fields for embedded collection' do
35
+ @project.respond_to?(:task_custom_fields).should be_true
36
+ end
37
+
38
+ end
39
+
40
+ context '#building' do
41
+
42
+ before(:each) do
43
+ @project = Project.new
44
+ @project.task_custom_fields.build :label => 'Short summary', :_alias => 'summary', :kind => 'String'
45
+ @task = @project.tasks.build
46
+ end
47
+
48
+ it 'returns a new document whose Class is different from the original one' do
49
+ @task.class.should_not == Task
50
+ end
51
+
52
+ it 'returns a new document with custom field' do
53
+ @project.tasks.build
54
+ @project.tasks.build
55
+ @task.respond_to?(:summary).should be_true
56
+ end
57
+
58
+ it 'sets/gets custom attributes' do
59
+ @task.summary = 'Lorem ipsum...'
60
+ @task.summary.should == 'Lorem ipsum...'
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ context 'with related collection' do
68
+
69
+ context '#association' do
70
+
71
+ before(:each) do
72
+ @project = Project.new
73
+ end
74
+
75
+ it 'has custom fields for related collections' do
76
+ @project.respond_to?(:person_custom_fields).should be_true
77
+ end
78
+
79
+ end
80
+
81
+ context '#building' do
82
+
83
+ before(:each) do
84
+ @project = Project.new
85
+ @project.person_custom_fields.build :label => 'Position in the project', :_alias => 'position', :kind => 'String'
86
+ @person = @project.people.build
87
+ end
88
+
89
+ it 'returns a new document whose Class is different from the original one' do
90
+ @person.class.should_not == Person
91
+ end
92
+
93
+ it 'returns a new document with custom field' do
94
+ @person.respond_to?(:position).should be_true
95
+ end
96
+
97
+ it 'sets/gets custom attributes' do
98
+ @person.position = 'Designer'
99
+ @person.position.should == 'Designer'
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ end