degu 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .*.sw[pon]
2
+ .DS_Store
3
+ .rvmrc
4
+ Gemfile.lock
5
+ coverage
6
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # vim: set filetype=ruby et sw=2 ts=2:
2
+
3
+ source :rubygems
4
+
5
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,150 @@
1
+ = Degu
2
+
3
+ Degu bundles the renum Enumeration implementation with the has_enum and has_set
4
+ rails plugins.
5
+
6
+ == Renum
7
+
8
+ === Description
9
+
10
+ Renum provides a readable but terse enum facility for Ruby. Enums are
11
+ sometimes called object constants and are analogous to the type-safe enum
12
+ pattern in Java, though obviously Ruby's flexibility means there's no such
13
+ thing as type-safety.
14
+
15
+ == Usage
16
+
17
+ Renum allows you to do things like this:
18
+
19
+ enum :Status, %w( NOT_STARTED IN_PROGRESS COMPLETE )
20
+
21
+ enum :Size do
22
+ Small("Really really tiny")
23
+ Medium("Sort of in the middle")
24
+ Large("Quite big")
25
+
26
+ attr_reader :description
27
+
28
+ def init description
29
+ @description = description
30
+ end
31
+ end
32
+
33
+ module MyNamespace
34
+ enum :FooValue, [ :Bar, :Baz, :Bat ]
35
+ end
36
+
37
+ Giving you something that satisfies this spec, plus a bit more:
38
+
39
+ describe "enum" do
40
+
41
+ it "creates a class for the value type" do
42
+ Status.class.should == Class
43
+ end
44
+
45
+ it "makes each value an instance of the value type" do
46
+ Status::NOT_STARTED.class.should == Status
47
+ end
48
+
49
+ it "exposes array of values" do
50
+ Status.values.should == [Status::NOT_STARTED, Status::IN_PROGRESS, Status::COMPLETE]
51
+ end
52
+
53
+ it "provides an alternative means of declaring values where extra information can be provided for initialization" do
54
+ Size::Small.description.should == "Really really tiny"
55
+ end
56
+
57
+ it "enumerates over values" do
58
+ Status.map {|s| s.name}.should == %w[NOT_STARTED IN_PROGRESS COMPLETE]
59
+ end
60
+
61
+ it "indexes values" do
62
+ Status[2].should == Status::COMPLETE
63
+ end
64
+
65
+ it "provides index lookup on values" do
66
+ Status::IN_PROGRESS.index.should == 1
67
+ end
68
+
69
+ it "provides a reasonable to_s for values" do
70
+ Status::NOT_STARTED.to_s.should == "Status::NOT_STARTED"
71
+ end
72
+
73
+ it "makes values comparable" do
74
+ Status::NOT_STARTED.should < Status::COMPLETE
75
+ end
76
+
77
+ it "allows enums to be nested in other modules or classes" do
78
+ MyNamespace::FooValue::Bar.class.should == MyNamespace::FooValue
79
+ end
80
+
81
+ end
82
+
83
+ === Rails[http://www.rubyonrails.com/] Integration
84
+
85
+ To use enumerated values as ActiveRecord attribute values, use the
86
+ constantize_attribute plugin
87
+ (https://github.com/duelinmarkers/constantize_attribute/tree) (also by me).
88
+
89
+ class Vehicle < ActiveRecord::Base
90
+ enum :Status do
91
+ New()
92
+ Used()
93
+ Salvage(true)
94
+
95
+ def init(warn = false)
96
+ @warn = warn
97
+ end
98
+
99
+ def requires_warning_buyer?
100
+ @warn
101
+ end
102
+ end
103
+
104
+ constantize_attribute :status
105
+
106
+ end
107
+
108
+ v = Vehicle.create! :status => Vehicle::Status::New
109
+ # Now the database has the string "Vehicle::Status::New",
110
+ # but your record object exposes the Status object:
111
+ v.status.requires_warning_buyer? # => false
112
+
113
+ v.update_attribute :status, Vehicle::Status::Salvage
114
+ # Now the database has the string "Vehicle::Status::Salvage".
115
+ v.status.requires_warning_buyer? # => true
116
+
117
+ # Since constantize_attribute also accepts strings, it's easy
118
+ # to use enumerated values with forms.
119
+ v.status = "Vehicle::Status::Used"
120
+ v.status.requires_warning_buyer? # => false
121
+
122
+ === License
123
+
124
+ This code is free to use under the terms of the MIT license.
125
+
126
+ Permission is hereby granted, free of charge, to any person obtaining
127
+ a copy of this software and associated documentation files (the
128
+ "Software"), to deal in the Software without restriction, including
129
+ without limitation the rights to use, copy, modify, merge, publish,
130
+ distribute, sublicense, and/or sell copies of the Software, and to
131
+ permit persons to whom the Software is furnished to do so, subject to
132
+ the following conditions:
133
+
134
+ The above copyright notice and this permission notice shall be
135
+ included in all copies or substantial portions of the Software.
136
+
137
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
138
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
139
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
140
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
141
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
142
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
143
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
144
+
145
+ === Contact
146
+
147
+ Renum was created by John D. Hume. Comments are welcome. Send an email to
148
+ duelin dot markers at gmail or "contact me via my
149
+ blog[http://elhumidor.blogspot.com/].
150
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ # vim: set filetype=ruby et sw=2 ts=2:
2
+
3
+ require 'rspec'
4
+ require 'gem_hadar'
5
+
6
+ GemHadar do
7
+ name 'degu'
8
+ author 'Florian Frank'
9
+ email 'dev@pkw.de'
10
+ homepage "http://github.com/caroo/#{name}"
11
+ summary 'Library for enums and bitfield sets.'
12
+ description 'Library that includes enums, and rails support for enums and bitfield sets.'
13
+ test_dir 'test'
14
+ test_files Dir['test/**/*_test.rb']
15
+ spec_dir 'spec'
16
+ ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.rvmrc', 'coverage', '.DS_Store'
17
+ readme 'README.rdoc'
18
+
19
+ dependency 'activerecord', '~> 3.0'
20
+
21
+ development_dependency 'mocha'
22
+ development_dependency 'sqlite3'
23
+ development_dependency 'rspec'
24
+ end
25
+
26
+ desc 'Run specs and tests'
27
+ task :default => [ :test, :spec ]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/degu.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "degu"
5
+ s.version = "0.0.4"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Florian Frank"]
9
+ s.date = "2011-12-08"
10
+ s.description = "Library that includes enums, and rails support for enums and bitfield sets."
11
+ s.email = "dev@pkw.de"
12
+ s.extra_rdoc_files = ["README.rdoc", "lib/degu/has_enum.rb", "lib/degu/has_set.rb", "lib/degu/polite.rb", "lib/degu/renum/enumerated_value.rb", "lib/degu/renum/enumerated_value_type_factory.rb", "lib/degu/renum.rb", "lib/degu/rude.rb", "lib/degu/version.rb", "lib/degu.rb"]
13
+ s.files = [".gitignore", "Gemfile", "README.rdoc", "Rakefile", "VERSION", "degu.gemspec", "lib/degu.rb", "lib/degu/has_enum.rb", "lib/degu/has_set.rb", "lib/degu/polite.rb", "lib/degu/renum.rb", "lib/degu/renum/enumerated_value.rb", "lib/degu/renum/enumerated_value_type_factory.rb", "lib/degu/rude.rb", "lib/degu/version.rb", "spec/renum_spec.rb", "spec/spec_helper.rb", "test/has_enum_test.rb", "test/has_set_test.rb", "test/test_helper.rb", "test_helper.rb"]
14
+ s.homepage = "http://github.com/caroo/degu"
15
+ s.rdoc_options = ["--title", "Degu - Library for enums and bitfield sets.", "--main", "README.rdoc"]
16
+ s.require_paths = ["lib"]
17
+ s.rubygems_version = "1.8.11"
18
+ s.summary = "Library for enums and bitfield sets."
19
+ s.test_files = ["test/has_enum_test.rb", "test/has_set_test.rb"]
20
+
21
+ if s.respond_to? :specification_version then
22
+ s.specification_version = 3
23
+
24
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
+ s.add_development_dependency(%q<gem_hadar>, ["~> 0.1.3"])
26
+ s.add_development_dependency(%q<mocha>, [">= 0"])
27
+ s.add_development_dependency(%q<sqlite3>, [">= 0"])
28
+ s.add_development_dependency(%q<rspec>, [">= 0"])
29
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.0"])
30
+ else
31
+ s.add_dependency(%q<gem_hadar>, ["~> 0.1.3"])
32
+ s.add_dependency(%q<mocha>, [">= 0"])
33
+ s.add_dependency(%q<sqlite3>, [">= 0"])
34
+ s.add_dependency(%q<rspec>, [">= 0"])
35
+ s.add_dependency(%q<activerecord>, ["~> 3.0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<gem_hadar>, ["~> 0.1.3"])
39
+ s.add_dependency(%q<mocha>, [">= 0"])
40
+ s.add_dependency(%q<sqlite3>, [">= 0"])
41
+ s.add_dependency(%q<rspec>, [">= 0"])
42
+ s.add_dependency(%q<activerecord>, ["~> 3.0"])
43
+ end
44
+ end
@@ -0,0 +1,79 @@
1
+ require "degu/renum"
2
+
3
+ module Degu
4
+ module HasEnum
5
+ def self.included(modul)
6
+ modul.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ # Use like this
12
+ #
13
+ # class Furniture
14
+ # has_enum :colors, :column_name => :custom_color_type
15
+ # end
16
+ def has_enum(enum_name, options={})
17
+
18
+ enum_column = options.has_key?(:column_name) ? options[:column_name].to_s : "#{enum_name}_type"
19
+
20
+ self.send("validate", "#{enum_column}_check_for_valid_type_of_enum")
21
+
22
+ # throws a NameError if Enum Class doesn't exists
23
+ enum_class = options.has_key?(:class_name) ? options[:class_name].to_s.constantize : enum_name.to_s.camelize.constantize
24
+
25
+ # Enum must be a Renum::EnumeratedValue Enum
26
+ raise ArgumentError, "expected Renum::EnumeratedValue" unless enum_class.superclass == Renum::EnumeratedValue
27
+
28
+ define_method("reset_enum_changed") do
29
+ @enum_changed = false
30
+ end
31
+ after_save :reset_enum_changed
32
+
33
+ define_method("#{enum_name}") do
34
+ begin
35
+ return self[enum_column].present? ? enum_class.const_get(self[enum_column]) : nil
36
+ rescue NameError => e
37
+ return nil
38
+ end
39
+ end
40
+
41
+ define_method("#{enum_column}=") do |enum_literal|
42
+ unless enum_literal == self[enum_column]
43
+ self[enum_column] = enum_literal
44
+ @enum_changed = true
45
+ end
46
+ end
47
+
48
+ define_method("#{enum_name}=") do |enum_to_set|
49
+ old_value = self[enum_column]
50
+ enum_resolved = enum_class[enum_to_set]
51
+ if enum_to_set.to_s.strip.empty?
52
+ self[enum_column] = nil
53
+ elsif enum_resolved
54
+ self[enum_column] = enum_resolved.name
55
+ else
56
+ raise ArgumentError, "could not resolve #{enum_to_set.inspect}"
57
+ end
58
+ @enum_changed ||= self[enum_column] != old_value
59
+ end
60
+
61
+ define_method("#{enum_name}_has_changed?") do
62
+ !!@enum_changed
63
+ end
64
+
65
+ define_method("#{enum_column}_check_for_valid_type_of_enum") do
66
+ return true if self[enum_column].nil? || self[enum_column].to_s.empty?
67
+ begin
68
+ enum_class.const_get(self[enum_column])
69
+ rescue NameError => e
70
+ self.errors.add(enum_column.to_sym, "Wrong type '#{self[enum_column]}' for enum '#{enum_name}'")
71
+ return false
72
+ end
73
+ return true
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,110 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'active_support'
5
+ require 'active_record'
6
+ module Degu
7
+ module HasSet
8
+ VERSION = '0.0.4'
9
+
10
+ module ClassMethods
11
+ # Use like this:
12
+ #
13
+ # class Person < ActiveRecord::Base
14
+ # has_set :interests
15
+ # end
16
+ def has_set(set_name, options = {})
17
+
18
+ set_column = options.has_key?(:column_name) ? options[:column_name].to_s : "#{set_name}_bitfield"
19
+
20
+ begin
21
+ enum_class = options.has_key?(:enum_class) ? options[:enum_class] : set_name.to_s.camelcase.constantize
22
+ rescue NameError => ne
23
+ raise NameError, "There ist no class to take the set entries from (#{ne.message})."
24
+ end
25
+
26
+ # Extend enum_class with field_name method
27
+ enum_class.class_eval <<-EOF
28
+ def field_name
29
+ '#{set_name.to_s.singularize}_' + self.name.underscore
30
+ end
31
+ EOF
32
+
33
+ define_method("#{set_name}=") do |argument_value|
34
+ self[set_column] =
35
+ unless argument_value.nil?
36
+ set_elements =
37
+ if String === argument_value
38
+ argument_value.split(',').map(&:strip)
39
+ else
40
+ Array(argument_value)
41
+ end.map do |set_element|
42
+ enum_class[set_element]
43
+ end
44
+ set_elements.all? or raise ArgumentError, "element #{argument_value.inspect} contains invalid elements"
45
+ value = 0
46
+ set_elements.each do |set_element|
47
+ mask = 1 << set_element.bitfield_index
48
+ if mask & value == mask
49
+ next
50
+ else
51
+ value |= mask
52
+ end
53
+ end
54
+ value
55
+ end
56
+ end
57
+
58
+ define_method(set_name) do
59
+ value = self[set_column]
60
+ case
61
+ when value.blank?
62
+ ;;
63
+ when value.zero?
64
+ []
65
+ else
66
+ set_elements = enum_class.values.select do |enum_element|
67
+ send("#{set_name.to_s.singularize}_#{enum_element.name.underscore}?")
68
+ end
69
+ # special to_s method for element-array
70
+ class << set_elements
71
+ def to_s
72
+ map(&:name) * ', '
73
+ end
74
+ end
75
+ set_elements
76
+ end
77
+ end
78
+
79
+ # TODO: This should be a class method
80
+ define_method("available_#{set_name}") do
81
+ self.methods.grep(/#{set_name.to_s.singularize}_\w+[^\?=]$/).sort.map(&:to_s)
82
+ end
83
+
84
+ enum_class.values.each do |enum|
85
+ define_method("#{set_name.to_s.singularize}_#{enum.name.underscore}?") do
86
+ mask = 1 << enum.bitfield_index
87
+ self[set_column] & mask == mask
88
+ end
89
+
90
+ alias_method :"#{set_name.to_s.singularize}_#{enum.name.underscore}", :"#{set_name.to_s.singularize}_#{enum.name.underscore}?"
91
+
92
+ define_method("#{set_name.to_s.singularize}_#{enum.name.underscore}=") do |true_or_false|
93
+ mask = 1 << enum.bitfield_index
94
+ current_value = mask & self[set_column] == mask
95
+ true_or_false = true if true_or_false.to_s == "true" || true_or_false.respond_to?(:to_i) && true_or_false.to_i == 1
96
+ true_or_false = false if true_or_false.to_s == "false" || true_or_false.respond_to?(:to_i) && true_or_false.to_i == 0
97
+
98
+ if current_value != true_or_false
99
+ true_or_false ? self[set_column] |= mask : self[set_column] &= ~mask
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def self.included(modul)
107
+ modul.extend(ClassMethods)
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,6 @@
1
+ module Degu
2
+ end
3
+ require 'degu/version'
4
+ require 'degu/renum'
5
+ require 'degu/has_set'
6
+ require 'degu/has_enum'
@@ -0,0 +1,226 @@
1
+ require 'forwardable'
2
+ begin
3
+ require 'json'
4
+ rescue LoadError
5
+ end
6
+
7
+ module Degu
8
+ module Renum
9
+
10
+ # This is the superclass of all enumeration classes.
11
+ # An enumeration class is Enumerable over its values and exposes them by numeric index via [].
12
+ # Values are also comparable, sorting into the order in which they're declared.
13
+ class EnumeratedValue
14
+
15
+ class << self
16
+ include Enumerable
17
+ extend Forwardable
18
+
19
+ def_delegators :values, :first, :last, :each
20
+
21
+ # Returns an array of values in the order they're declared.
22
+ def values
23
+ @values ||= []
24
+ end
25
+
26
+ alias all values
27
+
28
+ # This class encapsulates an enum field (аctually a method with arity == 0).
29
+ class Field < Struct.new('Field', :name, :options, :block)
30
+ # Returns true if the :default option was given.
31
+ def default?
32
+ options.key?(:default)
33
+ end
34
+
35
+ # Returns the value of the :default option.
36
+ def default
37
+ options[:default]
38
+ end
39
+
40
+ # Returns true if a block was given.
41
+ def block?
42
+ !!block
43
+ end
44
+
45
+ # Determine the default value for the enum value +obj+ if +options+ is
46
+ # the options hash given to the init method.
47
+ def default_value(obj, options)
48
+ field_value = options[name]
49
+ if field_value.nil?
50
+ if default?
51
+ default_value = default
52
+ elsif block?
53
+ default_value = block[obj]
54
+ end
55
+ else
56
+ field_value
57
+ end
58
+ end
59
+
60
+ # Returns the name as a string.
61
+ def to_s
62
+ name.to_s
63
+ end
64
+
65
+ # Returns a detailed string representation of this field.
66
+ def inspect
67
+ "#<#{self.class}: #{self} #{options.inspect}>"
68
+ end
69
+ end
70
+
71
+ # Returns an array of all fields defined on this enum.
72
+ def fields
73
+ @fields ||= []
74
+ end
75
+
76
+ # Defines a field with the name +name+, the options +options+ and the
77
+ # block +block+. The only valid option at the moment is :default which is
78
+ # the default value the field is initialized with.
79
+ def field(name, options = {}, &block)
80
+ name = name.to_sym
81
+ fields.delete_if { |f| f.name == name }
82
+ fields << field = Field.new(name, options, block)
83
+ instance_eval { attr_reader field.name }
84
+ end
85
+
86
+ # Returns the value with the name +name+ and returns it.
87
+ def with_name name
88
+ values_by_name[name.to_s]
89
+ end
90
+
91
+ # Returns a hash that maps names to their respective values values.
92
+ def values_by_name
93
+ @values_by_name ||= values.inject({}) do |memo, value|
94
+ memo[value.name] = value
95
+ memo
96
+ end.freeze
97
+ end
98
+
99
+ # Returns the enum value for +index+. If +index+ is an Integer the
100
+ # index-th enum value is returned. Otherwise +index+ is converted into a
101
+ # String. For strings that start with a capital letter the with_name
102
+ # method is used to determine the enum value with the name +index+. If
103
+ # the string starts with a lowercase letter it is converted into
104
+ # camelcase first, that is foo_bar will be converted into FooBar, before
105
+ # with_name is called with this new value.
106
+ def [](index)
107
+ case index
108
+ when Integer
109
+ values[index]
110
+ when self
111
+ values[index.index]
112
+ else
113
+ name = index.to_s
114
+ case name
115
+ when /\A(\d+)\Z/
116
+ return values[$1.to_i]
117
+ when /\A[a-z]/
118
+ name = name.gsub(/(?:\A|_)(.)/) { $1.upcase }
119
+ end
120
+ with_name(name)
121
+ end
122
+ end
123
+
124
+ # Returns the enum instance stored in the marshalled string +string+.
125
+ def _load(string)
126
+ with_name Marshal.load(string)
127
+ end
128
+
129
+ if defined?(::JSON)
130
+ # Fetches the correct enum determined by the deserialized JSON
131
+ # document.
132
+ def json_create(data)
133
+ JSON.deep_const_get(data[JSON.create_id])[data['name']]
134
+ end
135
+ end
136
+ end
137
+
138
+ include Comparable
139
+
140
+ # Name of this enumerated value as a string.
141
+ attr_reader :name
142
+
143
+ # Index of this enumerated value as an integer.
144
+ attr_reader :index
145
+
146
+ alias_method :id, :index
147
+
148
+ # Creates an enumerated value named +name+ with a unique autoincrementing
149
+ # index number.
150
+ def initialize name
151
+ @name = name.to_s.freeze
152
+ @index = self.class.values.size
153
+ self.class.values << self
154
+ end
155
+
156
+ # This is the standard init method method which has an arbitrary number of
157
+ # arguments. If the last argument is a Hash and its keys are defined fields
158
+ # their respective values will be used to initialize the fields. If you
159
+ # want to use this method from an enum and define your own custom init
160
+ # method there, don't forget to call super from your method.
161
+ def init(*args)
162
+ if Hash === options = args.last
163
+ for field in self.class.fields
164
+ instance_variable_set "@#{field}", field.default_value(self, options)
165
+ end
166
+ end
167
+ end
168
+
169
+ # Returns the fully qualified name of the constant referring to this value.
170
+ # Don't override this if you're using Renum with the constantize_attribute
171
+ # plugin, which relies on this behavior.
172
+ def to_s
173
+ "#{self.class}::#{name}"
174
+ end
175
+
176
+ # Sorts enumerated values into the order in which they're declared.
177
+ def <=> other
178
+ index <=> other.index
179
+ end
180
+
181
+ # Returns a marshalled string for this enum instance.
182
+ def _dump(limit = -1)
183
+ Marshal.dump(name, limit)
184
+ end
185
+
186
+ if defined?(::JSON)
187
+ # Set the given fields in the +obj+ hash
188
+ def set_fields(obj, fields)
189
+ fields.each do |f|
190
+ name = f.name
191
+ value = instance_variable_get("@#{name}")
192
+ value.nil? and next
193
+ obj[name] = value
194
+ end
195
+ end
196
+
197
+ # Returns an enum (actually more a reference to an enum) serialized as a
198
+ # JSON document.
199
+ def as_json(opts = {}, *a)
200
+ opts ||= {}
201
+ obj = {
202
+ JSON.create_id => self.class.name,
203
+ :name => name,
204
+ }
205
+ case fields_opt = opts[:fields]
206
+ when nil, false
207
+ when true
208
+ set_fields obj, self.class.fields
209
+ when Array
210
+ fields_opt = fields_opt.map(&:to_sym)
211
+ set_fields obj, self.class.fields.select { |field| fields_opt.include?(field.name) }
212
+ else
213
+ raise ArgumentError, "unexpected fields option #{fields_opt.inspect}"
214
+ end
215
+ obj.as_json(opts)
216
+ end
217
+
218
+ def to_json(opts, *a)
219
+ obj = as_json(opts)
220
+ opts.respond_to?(:fields) and opts.delete(:fields)
221
+ obj.to_json(opts, *a)
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end