has_custom_fields 0.0.5 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Has Custom Fields' do
4
+
5
+ context "with no fields defined" do
6
+
7
+ describe "class methods" do
8
+
9
+ it "raises an error if missing a scope to the has_custom_fields class" do
10
+ class TestUser < ActiveRecord::Base; end
11
+ expect {
12
+ TestUser.send(:has_custom_fields)
13
+ }.to raise_error(ArgumentError, 'Must define :scope => [] on the has_custom_fields class method')
14
+ end
15
+
16
+ it "returns an empty array" do
17
+ org = Organization.create!(:name => 'ABC Corp')
18
+ User.custom_field_fields(:organization, org.id).should == []
19
+ end
20
+
21
+ it "returns an empty array if the scoped object doesn't exist" do
22
+ User.custom_field_fields(:organization, nil).should == []
23
+ end
24
+
25
+ it "raises an exception if the scope doesn't exist" do
26
+ expect {
27
+ User.custom_field_fields(:something, nil)
28
+ }.to raise_error(HasCustomFields::InvalidScopeError, 'Class User does not have scope :something defined for has_custom_fields')
29
+ end
30
+
31
+ end
32
+
33
+ describe "instance methods" do
34
+
35
+ before(:each) do
36
+ @org = Organization.create!(:name => 'ABC Corp')
37
+ @user = User.create!(:name => 'Mikel')
38
+ end
39
+
40
+ it "raises an exception if the field does not exist" do
41
+ expect {
42
+ @user.custom_fields[:organization][@org.id]['High Potential']
43
+ }.to raise_error(ActiveRecord::RecordNotFound, 'No field High Potential for organization 1')
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ describe "creating fields" do
51
+
52
+ it "creates the fields" do
53
+ @org = Organization.create!(:name => 'ABC Corp')
54
+ expect {
55
+ UserField.create!(:organization_id => @org.id, :name => 'Value', :style => 'text')
56
+ }.to change(HasCustomFields::UserField, :count).by(1)
57
+ end
58
+
59
+ end
60
+
61
+ context "with fields defined" do
62
+
63
+ before(:each) do
64
+ @org = Organization.create!(:name => 'ABC Corp')
65
+ UserField.create!(:organization_id => @org.id, :name => 'Value', :style => 'text')
66
+ UserField.create!(:organization_id => @org.id, :name => 'Customer', :style => 'checkbox')
67
+ UserField.create!(:organization_id => @org.id, :name => 'Category', :style => 'select',
68
+ :user_field_select_options => [UserFieldSelectOption.create!(:option => 'CatA'),
69
+ UserFieldSelectOption.create!(:option => 'CatB'),
70
+ UserFieldSelectOption.create!(:option => 'CatC')])
71
+ end
72
+
73
+ describe "class methods" do
74
+
75
+ it "returns an array of UserFields" do
76
+ User.custom_field_fields(:organization, @org.id).length.should == 3
77
+ values = User.custom_field_fields(:organization, @org.id).map(&:name)
78
+ values.should include('Customer')
79
+ values.should include('Value')
80
+ values.should include('Category')
81
+ end
82
+
83
+ it "returns an array of select options" do
84
+ select_options = User.custom_field_fields(:organization, @org.id).last.user_field_select_options.map(&:option)
85
+ select_options.should == ["CatA","CatB","CatC"]
86
+ end
87
+
88
+ it "should return an array of select options" do
89
+ select_options = User.custom_field_fields(:organization, @org.id).last.select_options_data
90
+ select_options.should == ["CatA","CatB","CatC"]
91
+ end
92
+
93
+ it "should set up the has_many and belongs_to relationships" do
94
+ User.custom_field_fields(:organization, @org.id).first.respond_to?(:user_field_select_options).should == true
95
+ User.custom_field_fields(:organization, @org.id).last.user_field_select_options.first.respond_to?(:user_field).should == true
96
+ end
97
+
98
+ end
99
+
100
+ describe "instance methods" do
101
+
102
+ before(:each) do
103
+ @user = User.create!(:name => 'Mikel', :organization => @org)
104
+ end
105
+
106
+ it "sets attributes accessible on the custom_fields virtual attribute" do
107
+ @user.update_attributes(:name => 'Mikel', :email => 'mikel@example.org',
108
+ :custom_fields => {:organization => {@org.id => {'Value' => '10000'}}})
109
+ @user.name.should == 'Mikel'
110
+ @user.email.should == 'mikel@example.org'
111
+ @user.custom_fields[:organization][@org.id]['Value'].should_not be_nil
112
+ end
113
+
114
+ it "returns nil if there is no value defined" do
115
+ @user.custom_fields[:organization][@org.id]['Customer'].should be_nil
116
+ @user.custom_fields[:organization][@org.id]['Value'].should be_nil
117
+ end
118
+
119
+ it "sets the value of the field and persists it in the database" do
120
+ expect {
121
+ @user.update_attributes(:custom_fields => {:organization => {@org.id => {'Value' => '10000', 'Customer' => '1'}}})
122
+ @user.custom_fields[:organization][@org.id]['Customer'].should == '1'
123
+ @user.custom_fields[:organization][@org.id]['Value'].should == '10000'
124
+ }.to change(UserAttribute, :count).by(2)
125
+ end
126
+
127
+ it "does not persist in the database if the value is nil or blank" do
128
+ expect {
129
+ @user.update_attributes(:custom_fields => {:organization => {@org.id => {'Value' => '', 'Customer' => nil}}})
130
+ @user.custom_fields[:organization][@org.id]['Customer'].should be_nil
131
+ @user.custom_fields[:organization][@org.id]['Value'].should be_nil
132
+ }.to change(UserAttribute, :count).by(0)
133
+ end
134
+
135
+ it "deletes the value from the database if the value is nil or blank" do
136
+ @user.update_attributes(:custom_fields => {:organization => {@org.id => {'Value' => '10000', 'Customer' => '1'}}})
137
+
138
+ expect {
139
+ @user.update_attributes(:custom_fields => {:organization => {@org.id => {'Value' => '', 'Customer' => nil}}})
140
+ @user.custom_fields[:organization][@org.id]['Customer'].should be_nil
141
+ @user.custom_fields[:organization][@org.id]['Value'].should be_nil
142
+ }.to change(UserAttribute, :count).by(-2)
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+
150
+ end
@@ -1,34 +1,37 @@
1
- $:.unshift(File.dirname(__FILE__) + '/../lib')
1
+ require "active_support"
2
+ require "active_record"
3
+ require "database_cleaner"
2
4
 
3
- ENV["RAILS_ENV"] ||= "test"
5
+ ENV['debug'] = 'test' unless ENV['debug']
4
6
 
5
- require "rubygems"
6
- require 'spec'
7
- require File.expand_path(File.join(File.dirname(__FILE__), "../../../../config/environment"))
8
- require 'spec/rails'
9
- require 'active_record/fixtures'
7
+ # Establish DB Connection
8
+ config = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'db', 'database.yml')))
9
+ ActiveRecord::Base.configurations = {'test' => config[ENV['DB'] || 'sqlite3']}
10
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
10
11
 
11
- begin
12
- require 'ruby-debug'
13
- Debugger.start
14
- rescue LoadError
15
- end
12
+ # Load Test Schema into the Database
13
+ load(File.dirname(__FILE__) + "/db/schema.rb")
16
14
 
17
- require "acts_as_custom_fields"
15
+ require File.dirname(__FILE__) + '/../init'
18
16
 
19
- config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
20
- ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
21
- ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'mysql'])
17
+ # Load in the test models
22
18
 
23
- plugin_fixture_path = File.expand_path(File.dirname(__FILE__) + "/fixtures/")
24
- $LOAD_PATH.unshift(plugin_fixture_path)
19
+ require File.dirname(__FILE__) + '/test_models/user'
20
+ require File.dirname(__FILE__) + '/test_models/organization'
25
21
 
26
- Spec::Runner.configure do |config|
27
- config.use_transactional_fixtures = true
28
- config.use_instantiated_fixtures = false
29
- config.fixture_path = plugin_fixture_path
30
- end
22
+ RSpec.configure do |config|
23
+
24
+ config.before(:suite) do
25
+ DatabaseCleaner.strategy = :transaction
26
+ DatabaseCleaner.clean_with(:truncation)
27
+ end
31
28
 
32
- load(File.dirname(__FILE__) + "/schema.rb")
29
+ config.before(:each) do
30
+ DatabaseCleaner.start
31
+ end
33
32
 
34
- alias :doing :lambda
33
+ config.after(:each) do
34
+ DatabaseCleaner.clean
35
+ end
36
+
37
+ end
@@ -0,0 +1,3 @@
1
+ class Organization < ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,6 @@
1
+ class User < ActiveRecord::Base
2
+ belongs_to :organization
3
+ has_custom_fields :scopes => [:organization]
4
+
5
+ attr_accessible :name, :email
6
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_custom_fields
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 5
10
- version: 0.0.5
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - kylejginavan
@@ -15,11 +15,11 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-27 00:00:00 -06:00
18
+ date: 2012-02-21 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: builder
22
+ name: activerecord
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
@@ -28,10 +28,54 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  hash: 3
30
30
  segments:
31
+ - 3
32
+ - 1
31
33
  - 0
32
- version: "0"
34
+ version: 3.1.0
33
35
  type: :runtime
34
36
  version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: database_cleaner
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: sqlite3
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
35
79
  description: Uses a vertical schema to add custom fields.
36
80
  email: kylejginavan@gmail.com
37
81
  executables: []
@@ -40,34 +84,25 @@ extensions: []
40
84
 
41
85
  extra_rdoc_files:
42
86
  - LICENSE
43
- - README.rdoc
87
+ - README.md
44
88
  files:
45
- - LICENSE
46
- - README.rdoc
89
+ - Gemfile
90
+ - init.rb
47
91
  - Rakefile
48
- - SPECDOC
49
- - VERSION
92
+ - LICENSE
93
+ - README.md
50
94
  - has_custom_fields.gemspec
51
- - has_custom_fields.tmproj
52
- - lib/custom_fields/custom_field_base.rb
95
+ - lib/has_custom_fields/base.rb
96
+ - lib/has_custom_fields/class_methods.rb
97
+ - lib/has_custom_fields/instance_methods.rb
98
+ - lib/has_custom_fields/railtie.rb
99
+ - lib/has_custom_fields/version.rb
53
100
  - lib/has_custom_fields.rb
54
- - spec/database.yml
55
- - spec/debug.log
56
- - spec/fixtures/document.rb
57
- - spec/fixtures/people.yml
58
- - spec/fixtures/person.rb
59
- - spec/fixtures/person_contact_infos.yml
60
- - spec/fixtures/post.rb
61
- - spec/fixtures/post_attributes.yml
62
- - spec/fixtures/posts.yml
63
- - spec/fixtures/preference.rb
64
- - spec/fixtures/preferences.yml
65
- - spec/models/eav_model_with_no_arguments_spec.rb
66
- - spec/models/eav_model_with_options_spec.rb
67
- - spec/models/eav_validation_spec.rb
68
- - spec/rcov.opts
69
- - spec/schema.rb
70
- - spec/spec.opts
101
+ - spec/db/database.yml
102
+ - spec/db/schema.rb
103
+ - spec/test_models/user.rb
104
+ - spec/test_models/organization.rb
105
+ - spec/has_custom_fields_spec.rb
71
106
  - spec/spec_helper.rb
72
107
  has_rdoc: true
73
108
  homepage: http://github.com/kylejginavan/has_custom_fields
@@ -1,117 +0,0 @@
1
- HasCustomFields
2
- ==============
3
-
4
- HasCustomFields allow for the Entity-attribute-value model (EAV), also
5
- known as object-attribute-value model and open schema on any of your ActiveRecord
6
- models.
7
-
8
- = What is Entity-attribute-value model?
9
- Entity-attribute-value model (EAV) is a data model that is used in circumstances
10
- where the number of attributes (properties, parameters) that can be used to describe
11
- a thing (an "entity" or "object") is potentially very vast, but the number that will
12
- actually apply to a given entity is relatively modest.
13
-
14
- = Typical Problem
15
- A good example of this is where you need to store
16
- lots (possible hundreds) of optional attributes on an object. My typical
17
- reference example is when you have a User object. You want to store the
18
- user's preferences between sessions. Every search, sort, etc in your
19
- application you want to keep track of so when the user visits that section
20
- of the application again you can simply restore the display to how it was.
21
-
22
- So your controller might have:
23
-
24
- Project.find :all, :conditions => current_user.project_search,
25
- :order => current_user.project_order
26
-
27
- But there could be hundreds of these little attributes that you really don't
28
- want to store directly on the user object. It would make your table have too
29
- many columns so it would be too much of a pain to deal with. Also there might
30
- be performance problems. So instead you might do something like
31
- this:
32
-
33
- class User < ActiveRecord::Base
34
- has_many :preferences
35
- end
36
-
37
- class Preferences < ActiveRecord::Base
38
- belongs_to :user
39
- end
40
-
41
- Now simply give the Preference model a "name" and "value" column and you are
42
- set..... except this is now too complicated. To retrieve a attribute you will
43
- need to do something like:
44
-
45
- Project.find :all,
46
- :conditions => current_user.preferences.find_by_name('project_search').value,
47
- :order => current_user.preferences.find_by_name('project_order').value
48
-
49
- Sure you could fix this through a few methods on your model. But what about
50
- saving?
51
-
52
- current_user.preferences.create :name => 'project_search',
53
- :value => "lastname LIKE 'jones%'"
54
- current_user.preferences.create :name => 'project_order',
55
- :value => "name"
56
-
57
- Again this seems to much. Again we could add some methods to our model to
58
- make this simpler but do we want to do this on every model. NO! So instead
59
- we use this plugin which does everything for us.
60
-
61
- = Capabilities
62
-
63
- The HasCustomFields plugin is capable of modeling this problem in a intuitive
64
- way. Instead of having to deal with a related model you treat all attributes
65
- (both on the model and related) as if they are all on the model. The plugin
66
- will try to save all attributes to the model (normal ActiveRecord behavior)
67
- but if there is no column for an attribute it will try to save it to a
68
- related model whose purpose is to store these many sparsely populated
69
- attributes.
70
-
71
- The main design goals are:
72
-
73
- * Have the eav attributes feel like normal attributes. Simple gets and sets
74
- will add and remove records from the related model.
75
- * Allow for more than one related model. So for example on my User model I might
76
- have some eav behavior going into a contact_info table while others are
77
- going in a user_preferences table.
78
- * Allow a model to determine what a valid eav attribute is for a given
79
- related model so our model still can generate a NoMethodError.
80
-
81
- Example
82
- =======
83
-
84
- Will make the current class have eav behaviour.
85
-
86
- class Post < ActiveRecord::Base
87
- has_custom_field_behavior
88
- end
89
- post = Post.find_by_title 'hello world'
90
- puts "My post intro is: #{post.intro}"
91
- post.teaser = 'An awesome introduction to the blog'
92
- post.save
93
-
94
- The above example should work even though "intro" and "teaser" are not
95
- attributes on the Post model.
96
-
97
- = Installation
98
-
99
- ./script/plugin install acts_as_custom_field_model
100
-
101
- = RUNNING UNIT TESTS
102
-
103
- == Creating the test database
104
-
105
- The test databases will be created from the info specified in test/database.yml.
106
- Either change that file to match your database or change your database to
107
- match that file.
108
-
109
- == Running with Rake
110
-
111
- The easiest way to run the unit tests is through Rake. By default sqlite3
112
- will be the database run. Just change your env variable DB to be the database
113
- adaptor (specified in database.yml) that you want to use. The database and
114
- permissions must already be setup but the tables will be created for you
115
- from schema.rb.
116
-
117
- Copyright (c) 2008 Marcus Wyatt, released under the MIT license