can_be 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
@@ -1,11 +1,20 @@
1
- ## Version 0.1.0
1
+ ## [Version 0.3.0](https://github.com/mstarkman/can_be/tree/v0.3.0)
2
2
 
3
- Initial release.
3
+ * Allowed the options to be specified inside the can_be method call block
4
+ * Added a block processor to each of the `change_to` methods
5
+ * Added the ability to configure the relationship name
6
+ * Implemented a set of custom RSpec matcher
7
+ * History can be kept to easily get back to data after switching can_be types
8
+ * Fixed issue where calling a change_to method didn't destroy the original details record upon save
9
+
10
+ ## [Version 0.2.1](https://github.com/mstarkman/can_be/tree/v0.2.1)
11
+
12
+ Changed the way that the details models are added.
4
13
 
5
- ## Version 0.2.0
14
+ ## [Version 0.2.0](https://github.com/mstarkman/can_be/tree/v0.2.0)
6
15
 
7
16
  Added details functionality for storing custom fields per type.
8
17
 
9
- ## Version 0.2.1
18
+ ## [Version 0.1.0](https://github.com/mstarkman/can_be/tree/v0.1.0)
10
19
 
11
- Changed the way that the details models are added.
20
+ Initial release.
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ can_be (0.3.0)
5
+ activerecord (~> 3.1)
6
+ activesupport (~> 3.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (3.2.11)
12
+ activesupport (= 3.2.11)
13
+ builder (~> 3.0.0)
14
+ activerecord (3.2.11)
15
+ activemodel (= 3.2.11)
16
+ activesupport (= 3.2.11)
17
+ arel (~> 3.0.2)
18
+ tzinfo (~> 0.3.29)
19
+ activesupport (3.2.11)
20
+ i18n (~> 0.6)
21
+ multi_json (~> 1.0)
22
+ arel (3.0.2)
23
+ builder (3.0.4)
24
+ coderay (1.0.8)
25
+ database_cleaner (0.9.1)
26
+ diff-lcs (1.1.3)
27
+ i18n (0.6.1)
28
+ method_source (0.8.1)
29
+ multi_json (1.5.0)
30
+ pry (0.9.10)
31
+ coderay (~> 1.0.5)
32
+ method_source (~> 0.8)
33
+ slop (~> 3.3.1)
34
+ rake (10.0.2)
35
+ rspec (2.12.0)
36
+ rspec-core (~> 2.12.0)
37
+ rspec-expectations (~> 2.12.0)
38
+ rspec-mocks (~> 2.12.0)
39
+ rspec-core (2.12.0)
40
+ rspec-expectations (2.12.0)
41
+ diff-lcs (~> 1.1.3)
42
+ rspec-mocks (2.12.0)
43
+ slop (3.3.3)
44
+ sqlite3 (1.3.6)
45
+ tzinfo (0.3.35)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ can_be!
52
+ database_cleaner
53
+ pry
54
+ rake
55
+ rspec (~> 2.0)
56
+ sqlite3
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
- # CanBe [![Build Status](https://secure.travis-ci.org/mstarkman/can_be.png?branch=master)](https://travis-ci.org/mstarkman/can_be) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mstarkman/can_be)
1
+ # CanBe [![Build Status](https://secure.travis-ci.org/mstarkman/can_be.png?branch=master)](https://travis-ci.org/mstarkman/can_be) [![Gem Version](https://badge.fury.io/rb/can_be.png)](http://badge.fury.io/rb/can_be) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mstarkman/can_be)
2
2
 
3
- CanBe allows you to track the type of your ActiveRecord model in a consistent simple manner. With just a little configuration on your part, each type of record can contain different attributes that are specifc to that type of record. From a data modelling perspective this is preferred over [ActiveRecord STI](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance) since you will not have many columns in your database that have null values. Under the hood, CanBe uses one-to-one [Polymorphic Associations](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations) to accomplish the different attributes per type.
3
+ CanBe allows you to track the type of your ActiveRecord model in a consistent simple manner. With just a little configuration on your part, each type of record can contain different attributes that are specific to that type of record. From a data modeling perspective this is preferred over [ActiveRecord STI](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Single+table+inheritance) since you will not have many columns in your database that have null values. Under the hood, CanBe uses one-to-one [Polymorphic Associations](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations) to accomplish the different attributes per type.
4
+
5
+ Here is a blog post that will describe more of the rationale behind the CanBe gem: [http://blog.markstarkman.com/blog/2013/01/09/writing-my-first-rubygem-canbe/](http://blog.markstarkman.com/blog/2013/01/09/writing-my-first-rubygem-canbe/)
6
+
7
+ ## Versioning
8
+
9
+ I will be following [Semantic Versioning](http://semver.org/) as closely as possible. The `master` branch will be the latest development version and may not match the version of the code you are using. There is a git tag for each released version. The [CHANGELOG.md](https://github.com/mstarkman/can_be/blob/master/CHANGELOG.md) will contain the correct links to each version.
4
10
 
5
11
  ## Installation
6
12
 
@@ -8,13 +14,21 @@ Add this line to your application's Gemfile:
8
14
 
9
15
  gem 'can_be'
10
16
 
17
+ If you feel like living on the edge, you can add this to your applications' Gemfile:
18
+
19
+ gem 'can_be', git: "git://github.com/mstarkman/can_be.git"
20
+
11
21
  And then execute:
12
22
 
13
23
  $ bundle
14
24
 
15
- Or install it yourself as:
25
+ ## Documentation
26
+
27
+ The documentation for the basic implementation of CanBe can be found in this readme. Here is the documentation for the other features.
16
28
 
17
- $ gem install can_be
29
+ * [Different Attributes per CanBe Type (details)](https://github.com/mstarkman/can_be/blob/master/docs/details.md)
30
+ * [Keeping Details History When Changing CanBe Types](https://github.com/mstarkman/can_be/blob/master/docs/history.md)
31
+ * [Custom RSpec Matchers](https://github.com/mstarkman/can_be/blob/master/docs/rspec_matcher.md)
18
32
 
19
33
  ## Database Configuration (via migrations)
20
34
 
@@ -31,23 +45,7 @@ class AddCanBeTypeToAddresses < ActiveRecord::Migration
31
45
  end
32
46
  ```
33
47
 
34
- ### Details Configuration
35
-
36
- If you want to store different attributes (columns), there are some more columns that you will need to add to your model, `details_id` and `details_type`. These fields will be used to store the relationships to the details information. Indexing these columns is your choice.
37
-
38
- Example migration:
39
-
40
- ```ruby
41
- class AddCanBeDetailsToAddresses < ActiveRecord::Migration
42
- def change
43
- add_column :addresses, :details_id, :integer
44
- add_column :addresses, :details_type, :string
45
- add_index :addresses, [:details_id, :details_type]
46
- end
47
- end
48
- ```
49
-
50
- You will also need to create the models that will be used to represent the details attributes for each type. You will need to configure the model to be a details model be calling the `can_be_detail` method in your model. You do not need to specify a details model for each CanBe type if there are not any extra attributes required for that type.
48
+ **NOTE:** Examples of the database migrations can be found in the [`spec/support/schema.rb`](https://github.com/mstarkman/can_be/blob/master/spec/support/schema.rb) file.
51
49
 
52
50
  ## Model Configuration
53
51
 
@@ -73,44 +71,36 @@ class Person < ActiveRecord::Base
73
71
  end
74
72
  ```
75
73
 
76
- ### Details Model Configuration
77
-
78
- In order to wire up a model to be a CanBe details model, you will need to call the `can_be_detail` method on that model.
79
-
80
- ```ruby
81
- class HomeAddressDetail < ActiveRecord::Base
82
- can_be_detail :address
83
- end
84
- ```
85
- The `can_be_detail` method take in one parameter. The parameter is the link to the CanBe model. This must be a symbol that will reference the CanBe model. In order to create the proper symbol, you can execute the following into your Rails console: `<ModelName>.name.underscore.to_sym`. Here is an example: `Address.name.underscore.to_sym`. In the above example, this will be used for the `Address` CanBe model.
86
-
87
- You will also need to call the `add_details_model` method in the `can_be` block, passing in the CanBe type and a symbol that represets the details model class.
74
+ Or you can set the options in a block when calling `can_be`.
88
75
 
89
76
  ```ruby
90
- class Address < ActiveRecord::Base
91
- can_be :home_address, :work_address, :vacation_address do
92
- add_details_model :home_address, :home_address_detail
77
+ class Person < ActiveRecord::Base
78
+ can_be :male, :female do
79
+ field_name :gender
80
+ default_type :female
93
81
  end
94
82
  end
95
83
  ```
96
84
 
85
+ **NOTE:** Examples of the model configurations can be found in the [`spec/support/models.rb`](https://github.com/mstarkman/can_be/blob/master/spec/support/models.rb) file.
86
+
97
87
  ## Usage
98
88
 
99
89
  The CanBe gem will provide you a lot methods to handle your type processing in an easy and consistent manner.
100
90
 
101
91
  ### Instantiating New Models
102
92
 
103
- You can continue to instantiate your CanBe models by using the `new` method. When you do, CanBe will ensure that the type of the record is assigned the detault CanBe type for your model.
93
+ You can continue to instantiate your CanBe models by using the `new` method. When you do, CanBe will ensure that the type of the record is assigned the default CanBe type for your model.
104
94
 
105
95
  There are also some helper methods put on your model to make it easier to instantiate the type of model that you want. These methods will take the form of `new_<CanBe type>`. For example, you can call `Address.new_home_address`. These methods will take the same parameters as the base `new` method provided by ActiveRecord.
106
96
 
107
97
  ### Creating New Models
108
98
 
109
- You can continue to create your CanBe models by using the `create` method. When you do, CanBe will ensure that the type of the record is assigned the detault CanBe type for your model.
99
+ You can continue to create your CanBe models by using the `create` method. When you do, CanBe will ensure that the type of the record is assigned the default CanBe type for your model.
110
100
 
111
101
  There are also some helper methods put on your model to make it easier to create the type of model that you want. These methods will take the form of `create_<CanBe type>`. For example, you can call `Address.create_home_address`. These methods will take the same parameters as the base `create` method provided by ActiveRecord.
112
102
 
113
- ### Changing CanBe Types
103
+ ### Changing CanBe Types (the "change_to" methods)
114
104
 
115
105
  There are several ways to change the type of record that you are working with. You can access the `can_be_type` attribute (or other attribute if you specified the field to be used) and change the value directly.
116
106
 
@@ -120,9 +110,7 @@ You can change the type of record and not persist it immediately to the database
120
110
 
121
111
  If you want to change the type of the record and persist it to the database immediately, you can call the appropriate `change_to_<CanBe type>!` method. For example, this method call will change the type of record to `:work_address` and persist the change to the database: `Address.create.change_to_work_address!`
122
112
 
123
- There is a validator for the CanBe field, that will unsure that the CanBe field is set to one of the CanBe types before persisting the record.
124
-
125
- NOTE: that when you are changing the type of record the details record will be changed to the correct CanBe details record. New records will only be persisted to the database when the CanBe model is persisted. If you change the CanBe model to a type that does not have a corresponding details model, `nil` will be stored for the details.
113
+ There is a validator for the CanBe field, that will ensure that the CanBe field is set to one of the CanBe types before persisting the record.
126
114
 
127
115
  ### Boolean Evaluation
128
116
 
@@ -134,12 +122,6 @@ There are two ways to find specific types of records. You can use the `find_by_
134
122
 
135
123
  Methods are also defined on your CanBe model that will find all of the records for a specific CanBe type. These methods take the form of `<pluralized CanBe type>`. For example, `Address.home_addresses` would return all of the records with a type of `:home_address`.
136
124
 
137
- ### Accessing the Details
138
-
139
- If you want to access the details model, you can call the `details` method on your instance and the instance of your model will be returned. If the type of model that you are using does not have a details model, `nil` will be returned.
140
-
141
- When you persist your CanBe model to the database, your details model will automatically be persisted.
142
-
143
125
  ## Contributing
144
126
 
145
127
  1. Fork it
@@ -21,6 +21,7 @@ Gem::Specification.new do |gem|
21
21
  gem.add_development_dependency('sqlite3')
22
22
  gem.add_development_dependency('database_cleaner')
23
23
  gem.add_development_dependency('rake')
24
+ gem.add_development_dependency('pry')
24
25
 
25
26
  gem.add_dependency("activerecord", "~> 3.1")
26
27
  gem.add_dependency("activesupport", "~> 3.1")
@@ -0,0 +1,94 @@
1
+ # Different Attributes per CanBe Type (details) #
2
+
3
+ CanBe can all you to store different attributes (columns) for each CanBe type that you define. This document will show you the implementation details.
4
+
5
+ ## Database Migrations ##
6
+
7
+ If you want to store different attributes (columns), there are some more columns that you will need to add to your model, `details_id` and `details_type`. These fields will be used to store the relationships to the details information. Indexing these columns is your choice. Example migration:
8
+
9
+ ```ruby
10
+ class AddCanBeDetailsToAddresses < ActiveRecord::Migration
11
+ def change
12
+ add_column :addresses, :details_id, :integer
13
+ add_column :addresses, :details_type, :string
14
+ add_index :addresses, [:details_id, :details_type]
15
+ end
16
+ end
17
+ ```
18
+
19
+ You will also need to create the models that will be used to represent the details attributes for each type. You will need to configure the model to be a details model be calling the `can_be_detail` method in your model. You do not need to specify a details model for each CanBe type if there are not any extra attributes required for that type.
20
+
21
+ If you planning to use a different `details_name` for the relationship, you will need to change the names of the database columns to have the appropriate `id` and `type` columns. For example, if you wanted to use `:address_details` as the `details_name`, you would do this.
22
+
23
+ ```ruby
24
+ class AddCanBeDetailsToAddresses < ActiveRecord::Migration
25
+ def change
26
+ add_column :addresses, :address_details_id, :integer
27
+ add_column :addresses, :address_details_type, :string
28
+ add_index :addresses, [:address_details_id, :address_details_type]
29
+ end
30
+ end
31
+ ```
32
+
33
+ **NOTE:** Examples of the database migrations can be found in the [`spec/support/schema.rb`](https://github.com/mstarkman/can_be/blob/master/spec/support/schema.rb) file.
34
+
35
+ ## Models ##
36
+
37
+ In order to wire up a model to be a CanBe details model, you will need to call the `can_be_detail` method on that model.
38
+
39
+ ```ruby
40
+ class HomeAddressDetail < ActiveRecord::Base
41
+ can_be_detail :address
42
+ end
43
+ ```
44
+
45
+ The `can_be_detail` method takes in one parameter. The parameter is the link to the CanBe model. This must be a symbol that will reference the CanBe model. In order to create the proper symbol, you can execute the following into your Rails console: `<ModelName>.name.underscore.to_sym`. Here is an example: `Address.name.underscore.to_sym`. In the above example, this will be used for the `Address` CanBe model.
46
+
47
+ You will also need to call the `add_details_model` method in the `can_be` block, passing in the CanBe type and a symbol that represets the details model class.
48
+
49
+ ```ruby
50
+ class Address < ActiveRecord::Base
51
+ can_be :home_address, :work_address, :vacation_address do
52
+ add_details_model :home_address, :home_address_detail
53
+ end
54
+ end
55
+ ```
56
+
57
+ If you are using a different `details_name` for the relationship, you will also need to add the configuration options like this.
58
+
59
+ ```ruby
60
+ class Address < ActiveRecord::Base
61
+ can_be :home_address, :work_address, :vacation_address do
62
+ add_details_model :home_address, :home_address_detail
63
+ details_name :address_details
64
+ end
65
+ end
66
+ ```
67
+
68
+ ```ruby
69
+ class HomeAddressDetail < ActiveRecord::Base
70
+ can_be_detail :address, :address_details
71
+ end
72
+ ```
73
+
74
+ **NOTE:** Examples of the model configurations can be found in the [`spec/support/models.rb`](https://github.com/mstarkman/can_be/blob/master/spec/support/models.rb) file.
75
+
76
+ ## Accessing the Details
77
+
78
+ If you want to access the details model, you can call the `details` method on your instance and the instance of your model will be returned. If the type of model that you are using does not have a details model, `nil` will be returned.
79
+
80
+ When you persist your CanBe model to the database, your details model will automatically be persisted.
81
+
82
+ If you are using a different `details_name`, you will access the details by calling a method of the same name. For example, if you assigned `:address_details` to the `details_name, then you can access the details by calling the `address_details` method.
83
+
84
+ ## Calling the "change_to" methods
85
+
86
+ When you are changing the type of record the details record will be changed to the correct CanBe details record. New records will only be persisted to the database when the CanBe model is persisted. If you change the CanBe model to a type that does not have a corresponding details model, `nil` will be stored for the details.
87
+
88
+ Changing the CanBe type with the "change_to" methods can also accept a block to allow you to set the data on the new details record.
89
+
90
+ ```ruby
91
+ upload.change_to_image_upload do |details|
92
+ details.format = "jpeg"
93
+ end
94
+ ```
@@ -0,0 +1,71 @@
1
+ # Keeping Details History When Changing CanBe Types #
2
+
3
+ CanBe provides a history facility that will allow the [details](https://github.com/mstarkman/can_be/blob/master/docs/details.md) data to be preserved when switching between CanBe types. This way if you switch back to a CanBe type that was previously used, the specific data for the new CanBe type will still be available.
4
+
5
+ ## Database Migrations ##
6
+
7
+ To implement this history facility, there are no modifications necessary to your existing CanBe tables. You will only need to create a table (via migration) that will store the references for the CanBe types. All of the attribute (column) names **must** be named as shown in the example below.
8
+
9
+ ``` ruby
10
+ create_table :address_can_be_histories, :force => true do |t|
11
+ t.integer :can_be_model_id
12
+ t.string :can_be_type
13
+ t.integer :can_be_details_id
14
+ t.string :can_be_details_type
15
+ t.timestamps
16
+ end
17
+ ```
18
+
19
+ **NOTE:** Examples of the database migrations can be found in the [`spec/support/schema.rb`](https://github.com/mstarkman/can_be/blob/master/spec/support/schema.rb) file.
20
+
21
+ ## Models ##
22
+
23
+ There are only a few changes that are required to wire up the history model to the CanBe and CanBe details models.
24
+
25
+ First, you must create an Active Record model for the history table.
26
+
27
+ ``` ruby
28
+ class AddressHistory < ActiveRecord::Base
29
+ end
30
+ ```
31
+
32
+ ### CanBe Model ###
33
+
34
+ You will have to tell your current CanBe model to use the history model that you defined by adding a call to the `#keep_history_in` method.
35
+
36
+ ```ruby
37
+ class Address < ActiveRecord::Base
38
+ can_be :home_address, :work_address, :vacation_address do
39
+ add_details_model :home_address, :home_address_detail
40
+ keep_history_in :address_history
41
+ end
42
+ end
43
+ ```
44
+
45
+ ### CanBe Details Models ###
46
+
47
+ For each of the details models, you will also have to pass in the history model when calling the `#can_be_detail` method.
48
+
49
+ ```ruby
50
+ class HomeAddressDetail < ActiveRecord::Base
51
+ can_be_detail :address, history_model: :address_history
52
+ end
53
+ ```
54
+
55
+ **NOTE:** If you also want to change the `details_name` for the CanBe model, you will also need to specify that in the `options` parameter of the `#can_be_detail` method call.
56
+
57
+ ```ruby
58
+ class HomeAddressDetail < ActiveRecord::Base
59
+ can_be_detail :address, history_model: :address_history, details_name: :address_details
60
+ end
61
+ ```
62
+
63
+ ## Calling the "change_to" methods ##
64
+
65
+ When you are not storing history for a CanBe model and you call a "change_to" method, the details record that is no longer active will be destroyed. However, when you are storing the history, that record will *not* be destroy by default.
66
+
67
+ The "change_to" methods can now take in a parameter that, when set to `true`, will destroy the details record that is no longer active.
68
+
69
+ ## Access the CanBe model from an inactive details model ##
70
+
71
+ You can access the CanBe model from any details model that is in the database. This is done in the same way as accessing it from the active details model.
@@ -0,0 +1,11 @@
1
+ # Custom RSpec Matchers
2
+
3
+ Included in this gem are a set of matchers for RSpec that can be used to ensure the `can_be` and `can_be_detail` configuration in your models. To use these matchers, you will need to include this line in your `spec_helper.rb` file.
4
+
5
+ `require 'can_be/rspec/matchers'`
6
+
7
+ You can then use the `implement_can_be` matcher as follows. There are a number of fluent methods that you can use as well. These methods can be seen in the [`spec/can_be/rspec/matchers/can_be_matcher_spec.rb`](https://github.com/mstarkman/can_be/blob/master/spec/can_be/rspec/matchers/can_be_matcher_spec.rb) file.
8
+
9
+ `Address.should implement_can_be(:home_address, :work_address, :vacation_address)`
10
+
11
+ Examples of the `implement_can_be_detail` matcher can be found in the [`spec/can_be/rspec/matchers/can_be_detail_matcher_spec.rb`](https://github.com/mstarkman/can_be/blob/master/spec/can_be/rspec/matchers/can_be_detail_matcher_spec.rb) file.
@@ -15,6 +15,7 @@ module CanBe
15
15
  define_class_methods
16
16
  define_validations
17
17
  define_details
18
+ define_history
18
19
  end
19
20
 
20
21
  private
@@ -47,12 +48,12 @@ module CanBe
47
48
  can_be_processor.boolean_eval(t)
48
49
  end
49
50
 
50
- define_method "change_to_#{t}" do
51
- can_be_processor.update_field(t)
51
+ define_method "change_to_#{t}" do |force_history_removal = false, &block|
52
+ can_be_processor.update_field(t, force_history_removal: force_history_removal, &block)
52
53
  end
53
54
 
54
- define_method "change_to_#{t}!" do
55
- can_be_processor.update_field(t, true)
55
+ define_method "change_to_#{t}!" do |force_history_removal = false, &block|
56
+ can_be_processor.update_field(t, save: true, force_history_removal: force_history_removal, &block)
56
57
  end
57
58
  end
58
59
  end
@@ -94,11 +95,29 @@ module CanBe
94
95
 
95
96
  def define_details
96
97
  @klass.class_eval do
97
- belongs_to :details, polymorphic: true, autosave: true, dependent: :destroy
98
+ belongs_to self.can_be_config.details_name.to_sym, polymorphic: true, autosave: true, dependent: :destroy
98
99
 
99
100
  after_initialize do |model|
100
101
  model.can_be_processor.initialize_details
101
102
  end
103
+
104
+ after_save do |model|
105
+ model.can_be_processor.clean_details
106
+ end
107
+ end
108
+ end
109
+
110
+ def define_history
111
+ return unless @klass.can_be_config.keeps_history?
112
+
113
+ @klass.class_eval do
114
+ after_save do |model|
115
+ model.can_be_processor.save_history
116
+ end
117
+
118
+ after_destroy do |model|
119
+ model.can_be_processor.destroy_histories
120
+ end
102
121
  end
103
122
  end
104
123
  end