Create the illusion that an object has specific attributes when those attributes actually belong to an associated object.
This is particularly useful for different classes within a single- table inheritance table to have access to separate fields in class-specific associations.
Add this line to your application's Gemfile:
gem "external_fields"
And then execute:
$ bundle
Or install it yourself as:
$ gem install external_fields
Include ExternalFields
and define the external fields using the external_field
method. For example, if grade_level
, age
and credits
are defined in another class StudentData
and you want to access them in Student
you could do:
require "active_record"
require "active_support"
require "external_fields"
class Student < ActiveRecord::Base
include ExternalFields
has_one :data,
class_name: StudentData
external_field :grade_level, # External attribute 1
:age, # External attribute 2
:credits, # External attribute 3
:data, # Name of the association
class_name: "StudentData" # Class name of association
end
where the external fields are defined in another associated class:
class StudentData < ActiveRecord::Base
attr_accessor :grade_level, :age, :credits
end
Now you can directly call the accessors on the Student
objects:
> s = Student.create!
> s.age
=> nil
> s.age = 10
> s.age
=> 10
> s.grade_level = 4
> s.grade_level
=> 4
You can also add underscored accessors using the underscore
flag
...
external_field :grade_level, # External attribute 1
:age, # External attribute 2
:credits, # External attribute 3
:data, # Name of the association
class_name: "StudentData" # Class name of association
underscore: true # Flag for underscored accessors
...
This will allow you to use the external fields using underscored methods:
s = Student.create!
s._age
s._grade_level
This approach lets you override the default behavior cleanly. For example, you could override the grade level using this method:
def grade_level
if _grade_level == 0
"Kindergarten"
else
_grade_level
end
end
In some instances it's helpful to be able to use the original association
without building an object on access. For instance, you might want to have a
validation inspect a value without creating a new object on each save. In that
case, you can use the use_original
flag on the association like so:
validate :kindergarten_students_have_names
def kindergarten_students_have_names
data_obj = data(use_original: true)
if data_obj && grade_level == "Kindergarten" && name.blank?
# Note that `name` is an attribute on `Student` but `grade_level`
# is accessed through the `data` association as defined earlier
# in the README.
errors.add(:name, "must be present for kindergarten students")
end
end
We have documentation on RubyDoc.
- Fork it (https://github.com/panorama-ed/rails_external_fields/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Make sure your changes have appropriate tests (bundle exec rspec
)
and conform to the Rubocop style specified. We use
overcommit to enforce good code.
ExternalFields
is released under the
MIT License.