hobo 0.8.5 → 0.8.6
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.
- data/CHANGES.txt +41 -0
- data/Manifest +1 -5
- data/Rakefile +10 -3
- data/bin/hobo +38 -15
- data/dryml_generators/rapid/cards.dryml.erb +7 -7
- data/dryml_generators/rapid/pages.dryml.erb +52 -24
- data/hobo.gemspec +42 -322
- data/init.rb +0 -7
- data/lib/active_record/association_collection.rb +9 -0
- data/lib/hobo.rb +13 -14
- data/lib/hobo/accessible_associations.rb +32 -7
- data/lib/hobo/authentication_support.rb +1 -1
- data/lib/hobo/controller.rb +5 -7
- data/lib/hobo/dryml.rb +9 -2
- data/lib/hobo/dryml/dryml_builder.rb +11 -12
- data/lib/hobo/dryml/dryml_doc.rb +22 -24
- data/lib/hobo/dryml/dryml_generator.rb +41 -4
- data/lib/hobo/dryml/part_context.rb +5 -3
- data/lib/hobo/dryml/template.rb +7 -7
- data/lib/hobo/dryml/template_environment.rb +11 -22
- data/lib/hobo/dryml/template_handler.rb +94 -25
- data/lib/hobo/find_for.rb +2 -2
- data/lib/hobo/hobo_helper.rb +21 -21
- data/lib/hobo/include_in_save.rb +9 -5
- data/lib/hobo/lifecycles/transition.rb +2 -2
- data/lib/hobo/model.rb +11 -61
- data/lib/hobo/model_controller.rb +28 -29
- data/lib/hobo/model_router.rb +12 -13
- data/lib/hobo/permissions.rb +47 -37
- data/lib/hobo/permissions/associations.rb +1 -1
- data/lib/hobo/scopes/association_proxy_extensions.rb +5 -6
- data/lib/hobo/scopes/automatic_scopes.rb +7 -4
- data/lib/hobo/tasks/rails.rb +4 -0
- data/lib/hobo/user.rb +0 -1
- data/lib/hobo/user_controller.rb +3 -1
- data/lib/hobo/view_hints.rb +17 -3
- data/rails_generators/hobo/hobo_generator.rb +1 -0
- data/rails_generators/hobo_front_controller/templates/functional_test.rb +1 -11
- data/rails_generators/hobo_front_controller/templates/index.dryml +1 -6
- data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +1 -0
- data/rails_generators/hobo_rapid/templates/hobo-rapid.css +3 -2
- data/rails_generators/hobo_rapid/templates/hobo-rapid.js +24 -15
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +17 -12
- data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +6 -2
- data/rails_generators/hobo_rapid/templates/themes/clean/views/clean.dryml +2 -2
- data/rails_generators/hobo_user_model/templates/forgot_password.erb +2 -2
- data/rails_generators/hobo_user_model/templates/model.rb +2 -2
- data/taglibs/rapid.dryml +3 -2
- data/taglibs/rapid_core.dryml +21 -16
- data/taglibs/rapid_document_tags.dryml +1 -1
- data/taglibs/rapid_editing.dryml +7 -10
- data/taglibs/rapid_forms.dryml +115 -26
- data/taglibs/rapid_generics.dryml +13 -3
- data/taglibs/rapid_lifecycles.dryml +18 -1
- data/taglibs/rapid_navigation.dryml +50 -61
- data/taglibs/rapid_pages.dryml +103 -19
- data/taglibs/rapid_plus.dryml +54 -6
- data/taglibs/rapid_support.dryml +38 -1
- data/taglibs/rapid_user_pages.dryml +17 -5
- data/test/permissions/models/models.rb +24 -12
- data/test/permissions/models/test.sqlite3 +0 -0
- metadata +6 -15
- data/lib/extensions/test_case.rb +0 -129
- data/lib/hobo/composite_model.rb +0 -73
- data/lib/hobo/model_support.rb +0 -44
- data/tasks/fix_dryml.rake +0 -143
- data/tasks/generate_tag_reference.rake +0 -192
- data/test/dryml/complilation_test.rb +0 -261
data/lib/hobo/model.rb
CHANGED
@@ -6,7 +6,7 @@ module Hobo
|
|
6
6
|
|
7
7
|
NAME_FIELD_GUESS = %w(name title)
|
8
8
|
PRIMARY_CONTENT_GUESS = %w(description body content profile)
|
9
|
-
SEARCH_COLUMNS_GUESS = %w(name title body content profile)
|
9
|
+
SEARCH_COLUMNS_GUESS = %w(name title body description content profile)
|
10
10
|
|
11
11
|
|
12
12
|
def self.included(base)
|
@@ -86,7 +86,9 @@ module Hobo
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# ...but only return the ones that registered themselves
|
89
|
-
@model_names
|
89
|
+
@model_names.map do |name|
|
90
|
+
name.safe_constantize || (@model_names.delete name; nil)
|
91
|
+
end.compact
|
90
92
|
end
|
91
93
|
|
92
94
|
|
@@ -133,9 +135,6 @@ module Hobo
|
|
133
135
|
|
134
136
|
module ClassMethods
|
135
137
|
|
136
|
-
# include methods also shared by CompositeModel
|
137
|
-
#include ModelSupport::ClassMethods
|
138
|
-
|
139
138
|
attr_accessor :creator_attribute
|
140
139
|
attr_writer :name_attribute, :primary_content_attribute
|
141
140
|
|
@@ -204,7 +203,7 @@ module Hobo
|
|
204
203
|
if options[:polymorphic]
|
205
204
|
class_eval %{
|
206
205
|
def #{name}_is?(target)
|
207
|
-
target.
|
206
|
+
target.class.name == self.#{refl.options[:foreign_type]} && target.id == self.#{refl.primary_key_name}
|
208
207
|
end
|
209
208
|
def #{name}_changed?
|
210
209
|
#{refl.primary_key_name}_changed? || #{refl.options[:foreign_type]}_changed?
|
@@ -213,7 +212,7 @@ module Hobo
|
|
213
212
|
else
|
214
213
|
class_eval %{
|
215
214
|
def #{name}_is?(target)
|
216
|
-
target.id == self.#{refl.primary_key_name}
|
215
|
+
target.class == ::#{refl.klass.name} && target.id == self.#{refl.primary_key_name}
|
217
216
|
end
|
218
217
|
def #{name}_changed?
|
219
218
|
#{refl.primary_key_name}_changed?
|
@@ -359,10 +358,7 @@ module Hobo
|
|
359
358
|
|
360
359
|
def method_missing(name, *args, &block)
|
361
360
|
name = name.to_s
|
362
|
-
if name
|
363
|
-
# FIXME: Do we need this now?
|
364
|
-
call_method_chain(name, args, &block)
|
365
|
-
elsif create_automatic_scope(name)
|
361
|
+
if create_automatic_scope(name)
|
366
362
|
send(name.to_sym, *args, &block)
|
367
363
|
else
|
368
364
|
super(name.to_sym, *args, &block)
|
@@ -375,13 +371,6 @@ module Hobo
|
|
375
371
|
end
|
376
372
|
|
377
373
|
|
378
|
-
def call_method_chain(chain, args, &block)
|
379
|
-
parts = chain.split(".")
|
380
|
-
s = parts[0..-2].inject(self) { |m, scope| m.send(scope) }
|
381
|
-
s.send(parts.last, *args)
|
382
|
-
end
|
383
|
-
|
384
|
-
|
385
374
|
def to_url_path
|
386
375
|
"#{name.underscore.pluralize}"
|
387
376
|
end
|
@@ -412,7 +401,7 @@ module Hobo
|
|
412
401
|
def to_param
|
413
402
|
name_attr = self.class.name_attribute and name = send(name_attr)
|
414
403
|
if name_attr && !name.blank? && id.is_a?(Fixnum)
|
415
|
-
readable = name.to_s.downcase.gsub(/[^a-z0-9]+/, '-').
|
404
|
+
readable = name.to_s.downcase.gsub(/[^a-z0-9]+/, '-').remove(/-+$/).remove(/^-+/).split('-')[0..5].join('-')
|
416
405
|
@to_param ||= "#{id}-#{readable}"
|
417
406
|
else
|
418
407
|
id.to_s
|
@@ -431,6 +420,9 @@ module Hobo
|
|
431
420
|
end
|
432
421
|
|
433
422
|
|
423
|
+
# We deliberately give these three methods unconventional (java-esque) names to avoid
|
424
|
+
# polluting the application namespace
|
425
|
+
|
434
426
|
def set_creator(user)
|
435
427
|
set_creator!(user) unless get_creator
|
436
428
|
end
|
@@ -452,53 +444,11 @@ module Hobo
|
|
452
444
|
end
|
453
445
|
|
454
446
|
|
455
|
-
# We deliberately give this method an unconventional name to avoid
|
456
|
-
# polluting the application namespace too badly
|
457
447
|
def get_creator
|
458
448
|
self.class.creator_attribute && send(self.class.creator_attribute)
|
459
449
|
end
|
460
450
|
|
461
451
|
|
462
|
-
def duplicate
|
463
|
-
copy = self.class.new
|
464
|
-
copy.copy_instance_variables_from(self, ["@attributes_cache"])
|
465
|
-
copy.instance_variable_set("@attributes", @attributes.dup)
|
466
|
-
copy.instance_variable_set("@new_record", nil) unless new_record?
|
467
|
-
|
468
|
-
# Shallow copy of belongs_to associations
|
469
|
-
for refl in self.class.reflections.values
|
470
|
-
if refl.macro == :belongs_to and (target = self.send(refl.name))
|
471
|
-
bta = ActiveRecord::Associations::BelongsToAssociation.new(copy, refl)
|
472
|
-
bta.replace(target)
|
473
|
-
copy.instance_variable_set("@#{refl.name}", bta)
|
474
|
-
end
|
475
|
-
end
|
476
|
-
copy
|
477
|
-
end
|
478
|
-
|
479
|
-
|
480
|
-
def same_fields?(other, *fields)
|
481
|
-
return true if other.nil?
|
482
|
-
|
483
|
-
fields = fields.flatten
|
484
|
-
fields.all?{|f| self.send(f) == other.send(f)}
|
485
|
-
end
|
486
|
-
|
487
|
-
|
488
|
-
def only_changed_fields?(other, *changed_fields)
|
489
|
-
return true if other.nil?
|
490
|
-
|
491
|
-
changed_fields = changed_fields.flatten.*.to_s
|
492
|
-
all_cols = self.class.columns.*.name - []
|
493
|
-
all_cols.all?{|c| c.in?(changed_fields) || self.send(c) == other.send(c) }
|
494
|
-
end
|
495
|
-
|
496
|
-
|
497
|
-
def compose_with(object, use=nil)
|
498
|
-
CompositeModel.new_for([self, object])
|
499
|
-
end
|
500
|
-
|
501
|
-
|
502
452
|
def typed_id
|
503
453
|
"#{self.class.name.underscore}:#{self.id}" if id
|
504
454
|
end
|
@@ -4,10 +4,9 @@ module Hobo
|
|
4
4
|
|
5
5
|
include Hobo::Controller
|
6
6
|
|
7
|
-
VIEWLIB_DIR = "taglibs"
|
8
|
-
|
9
7
|
DONT_PAGINATE_FORMATS = [ Mime::CSV, Mime::YAML, Mime::JSON, Mime::XML, Mime::ATOM, Mime::RSS ]
|
10
|
-
|
8
|
+
|
9
|
+
WILL_PAGINATE_OPTIONS = [ :page, :per_page, :total_entries, :count, :finder ]
|
11
10
|
|
12
11
|
READ_ONLY_ACTIONS = [:index, :show]
|
13
12
|
WRITE_ONLY_ACTIONS = [:create, :update, :destroy]
|
@@ -27,7 +26,6 @@ module Hobo
|
|
27
26
|
|
28
27
|
helper_method :model, :current_user
|
29
28
|
before_filter :set_no_cache_headers
|
30
|
-
after_filter :remember_page_path
|
31
29
|
|
32
30
|
rescue_from ActiveRecord::RecordNotFound, :with => :not_found
|
33
31
|
|
@@ -35,7 +33,7 @@ module Hobo
|
|
35
33
|
rescue_from Hobo::Lifecycles::LifecycleKeyError, :with => :permission_denied
|
36
34
|
|
37
35
|
alias_method_chain :render, :hobo_model
|
38
|
-
|
36
|
+
|
39
37
|
end
|
40
38
|
register_controller(base)
|
41
39
|
|
@@ -97,7 +95,7 @@ module Hobo
|
|
97
95
|
index_action "complete_#{name}", &block
|
98
96
|
else
|
99
97
|
index_action "complete_#{name}" do
|
100
|
-
hobo_completions
|
98
|
+
hobo_completions field, model, options
|
101
99
|
end
|
102
100
|
end
|
103
101
|
end
|
@@ -109,8 +107,8 @@ module Hobo
|
|
109
107
|
got_block = block_given?
|
110
108
|
define_method web_name do
|
111
109
|
# Make sure we have a copy of the options - it is being mutated somewhere
|
112
|
-
opts =
|
113
|
-
self.this = find_instance(opts)
|
110
|
+
opts = options.dup
|
111
|
+
self.this = find_instance(opts)
|
114
112
|
raise Hobo::PermissionDeniedError unless @this.method_callable_by?(current_user, method)
|
115
113
|
if got_block
|
116
114
|
instance_eval(&block)
|
@@ -118,7 +116,7 @@ module Hobo
|
|
118
116
|
@this.send(method)
|
119
117
|
end
|
120
118
|
|
121
|
-
hobo_ajax_response
|
119
|
+
hobo_ajax_response unless performed?
|
122
120
|
end
|
123
121
|
end
|
124
122
|
|
@@ -370,7 +368,7 @@ module Hobo
|
|
370
368
|
after_submit = params[:after_submit]
|
371
369
|
|
372
370
|
# The after_submit post parameter takes priority
|
373
|
-
(after_submit == "stay-here" ?
|
371
|
+
(after_submit == "stay-here" ? url_for_page_path : after_submit) ||
|
374
372
|
|
375
373
|
# Then try the record's show page
|
376
374
|
(!destroyed && object_url(@this)) ||
|
@@ -385,6 +383,11 @@ module Hobo
|
|
385
383
|
home_page
|
386
384
|
end
|
387
385
|
|
386
|
+
|
387
|
+
def url_for_page_path
|
388
|
+
controller, view = Controller.controller_and_view_for(params[:page_path])
|
389
|
+
url_for :controller => controller, :action => view
|
390
|
+
end
|
388
391
|
|
389
392
|
# TODO: Get rid of this joke of an idea that fails miserably if you open another browser window.
|
390
393
|
def previous_page_path
|
@@ -410,10 +413,12 @@ module Hobo
|
|
410
413
|
|
411
414
|
def response_block(&b)
|
412
415
|
if b
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
416
|
+
respond_to do |format|
|
417
|
+
if b.arity == 1
|
418
|
+
yield format
|
419
|
+
else
|
420
|
+
format.html { yield }
|
421
|
+
end
|
417
422
|
end
|
418
423
|
performed?
|
419
424
|
end
|
@@ -421,7 +426,7 @@ module Hobo
|
|
421
426
|
|
422
427
|
|
423
428
|
def request_requires_pagination?
|
424
|
-
request.format.not_in?(DONT_PAGINATE_FORMATS)
|
429
|
+
request.format.not_in?(DONT_PAGINATE_FORMATS) && model.view_hints.paginate?
|
425
430
|
end
|
426
431
|
|
427
432
|
|
@@ -442,7 +447,7 @@ module Hobo
|
|
442
447
|
def find_owner_and_association(owner_association)
|
443
448
|
refl = model.reflections[owner_association]
|
444
449
|
klass = refl.klass
|
445
|
-
id = params["#{
|
450
|
+
id = params["#{owner_association}_id"]
|
446
451
|
owner = klass.find(id)
|
447
452
|
instance_variable_set("@#{owner_association}", owner)
|
448
453
|
[owner, owner.send(model.reverse_reflection(owner_association).name)]
|
@@ -470,7 +475,7 @@ module Hobo
|
|
470
475
|
|
471
476
|
def hobo_show(*args, &b)
|
472
477
|
options = args.extract_options!
|
473
|
-
self.this
|
478
|
+
self.this ||= find_instance(options)
|
474
479
|
response_block(&b)
|
475
480
|
end
|
476
481
|
|
@@ -490,7 +495,7 @@ module Hobo
|
|
490
495
|
|
491
496
|
def hobo_create(*args, &b)
|
492
497
|
options = args.extract_options!
|
493
|
-
self.this
|
498
|
+
self.this ||= args.first || new_for_create
|
494
499
|
this.user_update_attributes(current_user, options[:attributes] || attribute_parameters || {})
|
495
500
|
create_response(:new, &b)
|
496
501
|
end
|
@@ -499,7 +504,7 @@ module Hobo
|
|
499
504
|
def hobo_create_for(owner, *args, &b)
|
500
505
|
options = args.extract_options!
|
501
506
|
owner, association = find_owner_and_association(owner)
|
502
|
-
self.this
|
507
|
+
self.this ||= args.first || association.new
|
503
508
|
this.user_update_attributes(current_user, options[:attributes] || attribute_parameters || {})
|
504
509
|
create_response(:"new_for_#{owner}", &b)
|
505
510
|
end
|
@@ -547,7 +552,7 @@ module Hobo
|
|
547
552
|
def hobo_update(*args, &b)
|
548
553
|
options = args.extract_options!
|
549
554
|
|
550
|
-
self.this
|
555
|
+
self.this ||= args.first || find_instance
|
551
556
|
changes = options[:attributes] || attribute_parameters or raise RuntimeError, "No update specified in params"
|
552
557
|
this.user_update_attributes(current_user, changes)
|
553
558
|
|
@@ -594,7 +599,7 @@ module Hobo
|
|
594
599
|
|
595
600
|
def hobo_destroy(*args, &b)
|
596
601
|
options = args.extract_options!
|
597
|
-
self.this
|
602
|
+
self.this ||= args.first || find_instance
|
598
603
|
this.user_destroy(current_user)
|
599
604
|
flash[:notice] = "The #{model.name.titleize.downcase} was deleted" unless request.xhr?
|
600
605
|
destroy_response(&b)
|
@@ -671,7 +676,7 @@ module Hobo
|
|
671
676
|
options = options.reverse_merge(:limit => 10, :param => :query, :query_scope => "#{attribute}_contains")
|
672
677
|
finder = finder.limit(options[:limit]) unless finder.send(:scope, :find, :limit)
|
673
678
|
finder = finder.send(options[:query_scope], params[options[:param]])
|
674
|
-
items = finder.find(:all)
|
679
|
+
items = finder.find(:all).select { |r| r.viewable_by?(current_user) }
|
675
680
|
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attribute)}</li>\n"}.join + "</ul>"
|
676
681
|
end
|
677
682
|
|
@@ -694,6 +699,7 @@ module Hobo
|
|
694
699
|
|
695
700
|
def permission_denied(error)
|
696
701
|
self.this = true # Otherwise this gets sent user_view
|
702
|
+
@permission_error = error
|
697
703
|
if "permission_denied".in?(self.class.superclass.instance_methods)
|
698
704
|
super
|
699
705
|
else
|
@@ -762,13 +768,6 @@ module Hobo
|
|
762
768
|
headers["Expires"] ='0'
|
763
769
|
end
|
764
770
|
|
765
|
-
def remember_page_path
|
766
|
-
if request.method == :get
|
767
|
-
session[:previous_page_path] = request.path
|
768
|
-
session[:previous_page_path] += "?#{request.query_string}" unless request.query_string.blank?
|
769
|
-
end
|
770
|
-
end
|
771
|
-
|
772
771
|
# --- end filters --- #
|
773
772
|
|
774
773
|
public
|
data/lib/hobo/model_router.rb
CHANGED
@@ -73,20 +73,23 @@ module Hobo
|
|
73
73
|
return
|
74
74
|
end
|
75
75
|
|
76
|
-
|
77
|
-
|
76
|
+
begin
|
77
|
+
require "#{RAILS_ROOT}/app/controllers/application" unless Object.const_defined? :ApplicationController
|
78
|
+
rescue MissingSourceFile => ex
|
79
|
+
# must be on Rails 2.3. Yay!
|
80
|
+
end
|
81
|
+
|
78
82
|
# Add non-subsite, and all subsite routes
|
79
83
|
[nil, *Hobo.subsites].each { |subsite| add_routes_for(map, subsite) }
|
80
84
|
|
81
85
|
add_developer_routes(map) if Hobo.developer_features?
|
82
86
|
|
83
|
-
# Run the DRYML generators
|
84
|
-
# TODO: This needs a proper home
|
85
|
-
Hobo::Dryml::DrymlGenerator.run unless caller[-1] =~ /[\/\\]rake:\d+$/
|
86
87
|
rescue ActiveRecord::StatementInvalid => e
|
87
88
|
# Database problem? Just continue without routes
|
88
|
-
ActiveRecord::Base.logger
|
89
|
-
|
89
|
+
if ActiveRecord::Base.logger
|
90
|
+
ActiveRecord::Base.logger.warn "!! Database exception during Hobo routing -- continuing without routes"
|
91
|
+
ActiveRecord::Base.logger.warn "!! #{e.to_s}"
|
92
|
+
end
|
90
93
|
end
|
91
94
|
|
92
95
|
|
@@ -132,11 +135,7 @@ module Hobo
|
|
132
135
|
|
133
136
|
|
134
137
|
def add_routes
|
135
|
-
|
136
|
-
if model < Hobo::CompositeModel
|
137
|
-
map.connect "#{plural}/:id", :controller => plural, :action => 'show', :requirements => ID_REQUIREMENT
|
138
|
-
|
139
|
-
elsif controller < Hobo::ModelController
|
138
|
+
if controller < Hobo::ModelController
|
140
139
|
# index routes need to be first so the index names don't get
|
141
140
|
# taken as IDs
|
142
141
|
index_action_routes
|
@@ -181,7 +180,7 @@ module Hobo
|
|
181
180
|
|
182
181
|
owner = owner.to_s.singularize if model.reflections[owner].macro == :has_many
|
183
182
|
|
184
|
-
collection_path = "#{owner_class.pluralize}/:#{
|
183
|
+
collection_path = "#{owner_class.pluralize}/:#{owner}_id/#{collection}"
|
185
184
|
|
186
185
|
actions.each do |action|
|
187
186
|
case action
|
data/lib/hobo/permissions.rb
CHANGED
@@ -319,68 +319,78 @@ module Hobo
|
|
319
319
|
|
320
320
|
# By default, attempt to derive edit permission from create/update permission
|
321
321
|
def edit_permitted?(attribute)
|
322
|
-
if attribute
|
323
|
-
with_attribute_or_belongs_to_keys(attribute) do |attr, ftype|
|
324
|
-
unknownify_attribute(self, attr)
|
325
|
-
unknownify_attribute(self, ftype) if ftype
|
326
|
-
end
|
327
|
-
end
|
322
|
+
unknownify_attribute(attribute) if attribute
|
328
323
|
new_record? ? create_permitted? : update_permitted?
|
329
324
|
rescue Hobo::UndefinedAccessError
|
330
325
|
# The permission is dependent on the unknown value
|
331
326
|
# so this attribute is not editable
|
332
327
|
false
|
333
328
|
ensure
|
334
|
-
if attribute
|
335
|
-
with_attribute_or_belongs_to_keys(attribute) do |attr, ftype|
|
336
|
-
deunknownify_attribute(self, attr)
|
337
|
-
deunknownify_attribute(self, ftype) if ftype
|
338
|
-
end
|
339
|
-
end
|
329
|
+
deunknownify_attribute(attribute) if attribute
|
340
330
|
end
|
341
331
|
|
342
332
|
|
343
333
|
# Add some singleton methods to +record+ so give the effect that +attribute+ is unknown. That is,
|
344
334
|
# attempts to access the attribute will result in a Hobo::UndefinedAccessError
|
345
|
-
def unknownify_attribute(
|
346
|
-
|
347
|
-
|
335
|
+
def unknownify_attribute(attr)
|
336
|
+
metaclass.class_eval do
|
348
337
|
define_method attr do
|
349
338
|
raise Hobo::UndefinedAccessError
|
350
339
|
end
|
340
|
+
end
|
341
|
+
|
342
|
+
if (refl = self.class.reflections[attr.to_sym]) && refl.macro == :belongs_to
|
343
|
+
# A belongs_to -- also unknownify the underlying fields
|
344
|
+
unknownify_attribute refl.primary_key_name
|
345
|
+
unknownify_attribute refl.options[:foreign_type] if refl.options[:polymorphic]
|
346
|
+
else
|
347
|
+
# A regular field -- hack the dirty tracking methods
|
351
348
|
|
352
|
-
|
353
|
-
raise Hobo::UndefinedAccessError
|
354
|
-
end
|
349
|
+
metaclass.class_eval do
|
355
350
|
|
356
|
-
|
357
|
-
|
358
|
-
|
351
|
+
define_method "#{attr}_change" do
|
352
|
+
raise Hobo::UndefinedAccessError
|
353
|
+
end
|
359
354
|
|
360
|
-
|
361
|
-
|
362
|
-
|
355
|
+
define_method "#{attr}_was" do
|
356
|
+
read_attribute attr
|
357
|
+
end
|
358
|
+
|
359
|
+
define_method "#{attr}_changed?" do
|
360
|
+
true
|
361
|
+
end
|
363
362
|
|
364
|
-
|
365
|
-
|
366
|
-
|
363
|
+
def changed?
|
364
|
+
true
|
365
|
+
end
|
367
366
|
|
368
|
-
|
369
|
-
|
370
|
-
|
367
|
+
define_method :changed do
|
368
|
+
changed_attributes.keys | [attr.to_s]
|
369
|
+
end
|
371
370
|
|
372
|
-
|
373
|
-
|
374
|
-
|
371
|
+
def changes
|
372
|
+
raise Hobo::UndefinedAccessError
|
373
|
+
end
|
375
374
|
|
375
|
+
end
|
376
376
|
end
|
377
|
-
|
378
377
|
end
|
379
378
|
|
380
379
|
# Best. Name. Ever
|
381
|
-
def deunknownify_attribute(
|
382
|
-
|
383
|
-
|
380
|
+
def deunknownify_attribute(attr)
|
381
|
+
attr = attr.to_sym
|
382
|
+
|
383
|
+
metaclass.send :remove_method, attr
|
384
|
+
|
385
|
+
if (refl = self.class.reflections[attr]) && refl.macro == :belongs_to
|
386
|
+
# A belongs_to -- restore the underlying fields
|
387
|
+
deunknownify_attribute refl.primary_key_name
|
388
|
+
deunknownify_attribute refl.options[:foreign_type] if refl.options[:polymorphic]
|
389
|
+
else
|
390
|
+
# A regular field -- restore the dirty tracking methods
|
391
|
+
["#{attr}_change", "#{attr}_was", "#{attr}_changed?", :changed?, :changed, :changes].each do |m|
|
392
|
+
metaclass.send :remove_method, m.to_sym
|
393
|
+
end
|
384
394
|
end
|
385
395
|
end
|
386
396
|
end
|