cassandra_object 0.6.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/lib/cassandra_object/associations/one_to_many.rb +136 -0
  2. data/lib/cassandra_object/associations/one_to_one.rb +77 -0
  3. data/lib/cassandra_object/associations.rb +35 -0
  4. data/lib/cassandra_object/attributes.rb +93 -0
  5. data/lib/cassandra_object/base.rb +104 -0
  6. data/lib/cassandra_object/callbacks.rb +10 -0
  7. data/lib/cassandra_object/collection.rb +8 -0
  8. data/lib/cassandra_object/cursor.rb +86 -0
  9. data/lib/cassandra_object/dirty.rb +27 -0
  10. data/lib/cassandra_object/identity/abstract_key_factory.rb +36 -0
  11. data/lib/cassandra_object/identity/key.rb +20 -0
  12. data/lib/cassandra_object/identity/natural_key_factory.rb +51 -0
  13. data/lib/cassandra_object/identity/uuid_key_factory.rb +37 -0
  14. data/lib/cassandra_object/identity.rb +61 -0
  15. data/lib/cassandra_object/indexes.rb +129 -0
  16. data/lib/cassandra_object/legacy_callbacks.rb +33 -0
  17. data/lib/cassandra_object/migrations.rb +72 -0
  18. data/lib/cassandra_object/mocking.rb +15 -0
  19. data/lib/cassandra_object/persistence.rb +193 -0
  20. data/lib/cassandra_object/serialization.rb +6 -0
  21. data/lib/cassandra_object/type_registration.rb +7 -0
  22. data/lib/cassandra_object/types.rb +128 -0
  23. data/lib/cassandra_object/validation.rb +58 -0
  24. data/lib/cassandra_object.rb +30 -0
  25. data/vendor/active_support_shims.rb +4 -0
  26. data/vendor/activemodel/CHANGELOG +13 -0
  27. data/vendor/activemodel/CHANGES +12 -0
  28. data/vendor/activemodel/MIT-LICENSE +21 -0
  29. data/vendor/activemodel/README +21 -0
  30. data/vendor/activemodel/Rakefile +52 -0
  31. data/vendor/activemodel/activemodel.gemspec +19 -0
  32. data/vendor/activemodel/examples/validations.rb +29 -0
  33. data/vendor/activemodel/lib/active_model/attribute_methods.rb +291 -0
  34. data/vendor/activemodel/lib/active_model/callbacks.rb +91 -0
  35. data/vendor/activemodel/lib/active_model/conversion.rb +8 -0
  36. data/vendor/activemodel/lib/active_model/deprecated_error_methods.rb +33 -0
  37. data/vendor/activemodel/lib/active_model/dirty.rb +126 -0
  38. data/vendor/activemodel/lib/active_model/errors.rb +162 -0
  39. data/vendor/activemodel/lib/active_model/lint.rb +91 -0
  40. data/vendor/activemodel/lib/active_model/locale/en.yml +27 -0
  41. data/vendor/activemodel/lib/active_model/naming.rb +45 -0
  42. data/vendor/activemodel/lib/active_model/observing.rb +191 -0
  43. data/vendor/activemodel/lib/active_model/railtie.rb +2 -0
  44. data/vendor/activemodel/lib/active_model/serialization.rb +30 -0
  45. data/vendor/activemodel/lib/active_model/serializers/json.rb +96 -0
  46. data/vendor/activemodel/lib/active_model/serializers/xml.rb +204 -0
  47. data/vendor/activemodel/lib/active_model/state_machine/event.rb +62 -0
  48. data/vendor/activemodel/lib/active_model/state_machine/machine.rb +75 -0
  49. data/vendor/activemodel/lib/active_model/state_machine/state.rb +47 -0
  50. data/vendor/activemodel/lib/active_model/state_machine/state_transition.rb +40 -0
  51. data/vendor/activemodel/lib/active_model/state_machine.rb +70 -0
  52. data/vendor/activemodel/lib/active_model/test_case.rb +18 -0
  53. data/vendor/activemodel/lib/active_model/translation.rb +44 -0
  54. data/vendor/activemodel/lib/active_model/validations/acceptance.rb +55 -0
  55. data/vendor/activemodel/lib/active_model/validations/confirmation.rb +47 -0
  56. data/vendor/activemodel/lib/active_model/validations/exclusion.rb +42 -0
  57. data/vendor/activemodel/lib/active_model/validations/format.rb +64 -0
  58. data/vendor/activemodel/lib/active_model/validations/inclusion.rb +42 -0
  59. data/vendor/activemodel/lib/active_model/validations/length.rb +117 -0
  60. data/vendor/activemodel/lib/active_model/validations/numericality.rb +111 -0
  61. data/vendor/activemodel/lib/active_model/validations/presence.rb +42 -0
  62. data/vendor/activemodel/lib/active_model/validations/with.rb +59 -0
  63. data/vendor/activemodel/lib/active_model/validations.rb +120 -0
  64. data/vendor/activemodel/lib/active_model/validator.rb +110 -0
  65. data/vendor/activemodel/lib/active_model/version.rb +9 -0
  66. data/vendor/activemodel/lib/active_model.rb +61 -0
  67. data/vendor/activemodel/test/cases/attribute_methods_test.rb +46 -0
  68. data/vendor/activemodel/test/cases/callbacks_test.rb +70 -0
  69. data/vendor/activemodel/test/cases/helper.rb +23 -0
  70. data/vendor/activemodel/test/cases/lint_test.rb +28 -0
  71. data/vendor/activemodel/test/cases/naming_test.rb +28 -0
  72. data/vendor/activemodel/test/cases/observing_test.rb +133 -0
  73. data/vendor/activemodel/test/cases/serializeration/json_serialization_test.rb +83 -0
  74. data/vendor/activemodel/test/cases/serializeration/xml_serialization_test.rb +110 -0
  75. data/vendor/activemodel/test/cases/state_machine/event_test.rb +49 -0
  76. data/vendor/activemodel/test/cases/state_machine/machine_test.rb +43 -0
  77. data/vendor/activemodel/test/cases/state_machine/state_test.rb +72 -0
  78. data/vendor/activemodel/test/cases/state_machine/state_transition_test.rb +84 -0
  79. data/vendor/activemodel/test/cases/state_machine_test.rb +312 -0
  80. data/vendor/activemodel/test/cases/tests_database.rb +37 -0
  81. data/vendor/activemodel/test/cases/translation_test.rb +45 -0
  82. data/vendor/activemodel/test/cases/validations/acceptance_validation_test.rb +71 -0
  83. data/vendor/activemodel/test/cases/validations/conditional_validation_test.rb +141 -0
  84. data/vendor/activemodel/test/cases/validations/confirmation_validation_test.rb +58 -0
  85. data/vendor/activemodel/test/cases/validations/exclusion_validation_test.rb +47 -0
  86. data/vendor/activemodel/test/cases/validations/format_validation_test.rb +118 -0
  87. data/vendor/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +175 -0
  88. data/vendor/activemodel/test/cases/validations/i18n_validation_test.rb +527 -0
  89. data/vendor/activemodel/test/cases/validations/inclusion_validation_test.rb +71 -0
  90. data/vendor/activemodel/test/cases/validations/length_validation_test.rb +437 -0
  91. data/vendor/activemodel/test/cases/validations/numericality_validation_test.rb +180 -0
  92. data/vendor/activemodel/test/cases/validations/presence_validation_test.rb +70 -0
  93. data/vendor/activemodel/test/cases/validations/with_validation_test.rb +166 -0
  94. data/vendor/activemodel/test/cases/validations_test.rb +215 -0
  95. data/vendor/activemodel/test/config.rb +3 -0
  96. data/vendor/activemodel/test/fixtures/topics.yml +41 -0
  97. data/vendor/activemodel/test/models/contact.rb +7 -0
  98. data/vendor/activemodel/test/models/custom_reader.rb +17 -0
  99. data/vendor/activemodel/test/models/developer.rb +6 -0
  100. data/vendor/activemodel/test/models/person.rb +9 -0
  101. data/vendor/activemodel/test/models/reply.rb +34 -0
  102. data/vendor/activemodel/test/models/topic.rb +9 -0
  103. data/vendor/activemodel/test/models/track_back.rb +4 -0
  104. data/vendor/activemodel/test/schema.rb +14 -0
  105. data/vendor/activesupport/lib/active_support/autoload.rb +48 -0
  106. data/vendor/activesupport/lib/active_support/concern.rb +25 -0
  107. data/vendor/activesupport/lib/active_support/core_ext/array/wrap.rb +20 -0
  108. data/vendor/activesupport/lib/active_support/core_ext/object/blank.rb +58 -0
  109. data/vendor/activesupport/lib/active_support/core_ext/object/tap.rb +6 -0
  110. data/vendor/activesupport/lib/active_support/dependency_module.rb +17 -0
  111. data/vendor/activesupport/lib/active_support/i18n.rb +2 -0
  112. data/vendor/activesupport/lib/active_support/locale/en.yml +33 -0
  113. metadata +230 -0
@@ -0,0 +1,58 @@
1
+ module CassandraObject
2
+ module Validation
3
+ class RecordInvalidError < StandardError
4
+ attr_reader :record
5
+ def initialize(record)
6
+ @record = record
7
+ super("Invalid record: #{@record.errors.full_messages.to_sentence}")
8
+ end
9
+
10
+ def self.raise_error(record)
11
+ raise new(record)
12
+ end
13
+ end
14
+ extend ActiveSupport::Concern
15
+ include ActiveModel::Validations
16
+
17
+ included do
18
+ define_model_callbacks :validation
19
+ if CassandraObject.old_active_support
20
+ define_callbacks :validate
21
+ else
22
+ define_callbacks :validate, :scope => :name
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def create!(attributes)
28
+ returning new(attributes), &:save!
29
+ end
30
+ end
31
+
32
+ module InstanceMethods
33
+ def valid?
34
+ run_callbacks :validation do
35
+ super
36
+ end
37
+ end
38
+
39
+ def save
40
+ if valid?
41
+ super
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ def save!
48
+ save || RecordInvalidError.raise_error(self)
49
+ end
50
+
51
+ if CassandraObject.old_active_support
52
+ def _run_validate_callbacks
53
+ run_callbacks :validate
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'i18n'
3
+ require 'active_support'
4
+ require 'active_support/version'
5
+
6
+ module CassandraObject
7
+ class << self
8
+ attr_accessor :old_active_support
9
+ end
10
+ self.old_active_support = false
11
+ VERSION = "0.5.0"
12
+ end
13
+
14
+
15
+ if ActiveSupport::VERSION::STRING =~ /^2/
16
+
17
+ vendor = File.expand_path(File.dirname(__FILE__) + "/../vendor")
18
+ CassandraObject.old_active_support = true
19
+ $LOAD_PATH << vendor
20
+ require 'active_support_shims'
21
+ $LOAD_PATH << vendor + "/activemodel/lib"
22
+ require 'active_model'
23
+ else
24
+ require 'active_support/all'
25
+ require 'active_model'
26
+ end
27
+
28
+ require 'cassandra_object/base'
29
+
30
+
@@ -0,0 +1,4 @@
1
+ $: << File.dirname(__FILE__) + "/activesupport/lib"
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/autoload'
@@ -0,0 +1,13 @@
1
+ *Edge*
2
+
3
+ * Change the ActiveModel::Base.include_root_in_json default to true for Rails 3 [DHH]
4
+
5
+ * Add validates_format_of :without => /regexp/ option. #430 [Elliot Winkler, Peer Allan]
6
+
7
+ Example :
8
+
9
+ validates_format_of :subdomain, :without => /www|admin|mail/
10
+
11
+ * Introduce validates_with to encapsulate attribute validations in a class. #2630 [Jeff Dean]
12
+
13
+ * Extracted from Active Record and Active Resource.
@@ -0,0 +1,12 @@
1
+ Changes from extracting bits to ActiveModel
2
+
3
+ * ActiveModel::Observer#add_observer!
4
+
5
+ It has a custom hook to define after_find that should really be in a
6
+ ActiveRecord::Observer subclass:
7
+
8
+ def add_observer!(klass)
9
+ klass.add_observer(self)
10
+ klass.class_eval 'def after_find() end' unless
11
+ klass.respond_to?(:after_find)
12
+ end
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2004-2009 David Heinemeier Hansson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,21 @@
1
+ Active Model
2
+ ==============
3
+
4
+ Totally experimental library that aims to extract common model mixins from
5
+ ActiveRecord for use in ActiveResource (and other similar libraries).
6
+ This is in a very rough state (no autotest or spec rake tasks set up yet),
7
+ so please excuse the mess.
8
+
9
+ Here's what I plan to extract:
10
+ * ActiveModel::Observing
11
+ * ActiveModel::Callbacks
12
+ * ActiveModel::Validations
13
+
14
+ # for ActiveResource params and ActiveRecord options
15
+ * ActiveModel::Scoping
16
+
17
+ # to_json, to_xml, etc
18
+ * ActiveModel::Serialization
19
+
20
+ I'm trying to keep ActiveRecord compatibility where possible, but I'm
21
+ annotating the spots where I'm diverging a bit.
@@ -0,0 +1,52 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/lib/active_model/version"
3
+
4
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
5
+ PKG_NAME = 'activemodel'
6
+ PKG_VERSION = ActiveModel::VERSION::STRING + PKG_BUILD
7
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
8
+ RELEASE_NAME = "REL #{PKG_VERSION}"
9
+
10
+
11
+ require 'rake/testtask'
12
+
13
+ task :default => :test
14
+
15
+ Rake::TestTask.new do |t|
16
+ t.libs << "#{dir}/test"
17
+ t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort
18
+ t.warning = true
19
+ end
20
+
21
+ namespace :test do
22
+ task :isolated do
23
+ ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
24
+ Dir.glob("#{dir}/test/**/*_test.rb").all? do |file|
25
+ system(ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file)
26
+ end or raise "Failures"
27
+ end
28
+ end
29
+
30
+
31
+ require 'rake/rdoctask'
32
+
33
+ # Generate the RDoc documentation
34
+ Rake::RDocTask.new do |rdoc|
35
+ rdoc.rdoc_dir = "#{dir}/doc"
36
+ rdoc.title = "Active Model"
37
+ rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
38
+ rdoc.options << '--charset' << 'utf-8'
39
+ rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
40
+ rdoc.rdoc_files.include("#{dir}/README", "#{dir}/CHANGES")
41
+ rdoc.rdoc_files.include("#{dir}/lib/**/*.rb")
42
+ end
43
+
44
+
45
+ require 'rake/packagetask'
46
+ require 'rake/gempackagetask'
47
+
48
+ spec = eval(File.read("#{dir}/activemodel.gemspec"))
49
+
50
+ Rake::GemPackageTask.new(spec) do |p|
51
+ p.gem_spec = spec
52
+ end
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.platform = Gem::Platform::RUBY
3
+ s.name = 'activemodel'
4
+ s.version = '3.0.pre'
5
+ s.summary = "A toolkit for building other modeling frameworks like ActiveRecord"
6
+ s.description = %q{Extracts common modeling concerns from ActiveRecord to share between similar frameworks like ActiveResource.}
7
+
8
+ s.author = "David Heinemeier Hansson"
9
+ s.email = "david@loudthinking.com"
10
+ s.rubyforge_project = "activemodel"
11
+ s.homepage = "http://www.rubyonrails.org"
12
+
13
+ s.has_rdoc = true
14
+
15
+ s.add_dependency('activesupport', '= 3.0.pre')
16
+
17
+ s.require_path = 'lib'
18
+ s.files = Dir["CHANGELOG", "MIT-LICENSE", "README", "lib/**/*"]
19
+ end
@@ -0,0 +1,29 @@
1
+ require 'active_model'
2
+
3
+ class Person
4
+ include ActiveModel::Conversion
5
+ include ActiveModel::Validations
6
+
7
+ validates_presence_of :name
8
+
9
+ attr_accessor :name
10
+
11
+ def initialize(attributes = {})
12
+ @name = attributes[:name]
13
+ end
14
+
15
+ def persist
16
+ @persisted = true
17
+ end
18
+
19
+ def new_record?
20
+ @persisted
21
+ end
22
+ end
23
+
24
+ person1 = Person.new
25
+ p person1.valid?
26
+ person1.errors
27
+
28
+ person2 = Person.new(:name => "matz")
29
+ p person2.valid?
@@ -0,0 +1,291 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'active_support/core_ext/class/inheritable_attributes'
3
+
4
+ module ActiveModel
5
+ class MissingAttributeError < NoMethodError
6
+ end
7
+
8
+ module AttributeMethods
9
+ extend ActiveSupport::Concern
10
+
11
+ # Declare and check for suffixed attribute methods.
12
+ module ClassMethods
13
+ # Defines an "attribute" method (like +inheritance_column+ or
14
+ # +table_name+). A new (class) method will be created with the
15
+ # given name. If a value is specified, the new method will
16
+ # return that value (as a string). Otherwise, the given block
17
+ # will be used to compute the value of the method.
18
+ #
19
+ # The original method will be aliased, with the new name being
20
+ # prefixed with "original_". This allows the new method to
21
+ # access the original value.
22
+ #
23
+ # Example:
24
+ #
25
+ # class A < ActiveRecord::Base
26
+ # define_attr_method :primary_key, "sysid"
27
+ # define_attr_method( :inheritance_column ) do
28
+ # original_inheritance_column + "_id"
29
+ # end
30
+ # end
31
+ def define_attr_method(name, value=nil, &block)
32
+ sing = metaclass
33
+ sing.send :alias_method, "original_#{name}", name
34
+ if block_given?
35
+ sing.send :define_method, name, &block
36
+ else
37
+ # use eval instead of a block to work around a memory leak in dev
38
+ # mode in fcgi
39
+ sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
40
+ end
41
+ end
42
+
43
+ # Declares a method available for all attributes with the given prefix.
44
+ # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
45
+ #
46
+ # #{prefix}#{attr}(*args, &block)
47
+ #
48
+ # to
49
+ #
50
+ # #{prefix}attribute(#{attr}, *args, &block)
51
+ #
52
+ # An <tt>#{prefix}attribute</tt> instance method must exist and accept at least
53
+ # the +attr+ argument.
54
+ #
55
+ # For example:
56
+ #
57
+ # class Person < ActiveRecord::Base
58
+ # attribute_method_prefix 'clear_'
59
+ #
60
+ # private
61
+ # def clear_attribute(attr)
62
+ # ...
63
+ # end
64
+ # end
65
+ #
66
+ # person = Person.find(1)
67
+ # person.name # => 'Gem'
68
+ # person.clear_name
69
+ # person.name # => ''
70
+ def attribute_method_prefix(*prefixes)
71
+ attribute_method_matchers.concat(prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix })
72
+ undefine_attribute_methods
73
+ end
74
+
75
+ # Declares a method available for all attributes with the given suffix.
76
+ # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
77
+ #
78
+ # #{attr}#{suffix}(*args, &block)
79
+ #
80
+ # to
81
+ #
82
+ # attribute#{suffix}(#{attr}, *args, &block)
83
+ #
84
+ # An <tt>attribute#{suffix}</tt> instance method must exist and accept at least
85
+ # the +attr+ argument.
86
+ #
87
+ # For example:
88
+ #
89
+ # class Person < ActiveRecord::Base
90
+ # attribute_method_suffix '_short?'
91
+ #
92
+ # private
93
+ # def attribute_short?(attr)
94
+ # ...
95
+ # end
96
+ # end
97
+ #
98
+ # person = Person.find(1)
99
+ # person.name # => 'Gem'
100
+ # person.name_short? # => true
101
+ def attribute_method_suffix(*suffixes)
102
+ attribute_method_matchers.concat(suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix })
103
+ undefine_attribute_methods
104
+ end
105
+
106
+ # Declares a method available for all attributes with the given prefix
107
+ # and suffix. Uses +method_missing+ and <tt>respond_to?</tt> to rewrite
108
+ # the method.
109
+ #
110
+ # #{prefix}#{attr}#{suffix}(*args, &block)
111
+ #
112
+ # to
113
+ #
114
+ # #{prefix}attribute#{suffix}(#{attr}, *args, &block)
115
+ #
116
+ # An <tt>#{prefix}attribute#{suffix}</tt> instance method must exist and
117
+ # accept at least the +attr+ argument.
118
+ #
119
+ # For example:
120
+ #
121
+ # class Person < ActiveRecord::Base
122
+ # attribute_method_affix :prefix => 'reset_', :suffix => '_to_default!'
123
+ #
124
+ # private
125
+ # def reset_attribute_to_default!(attr)
126
+ # ...
127
+ # end
128
+ # end
129
+ #
130
+ # person = Person.find(1)
131
+ # person.name # => 'Gem'
132
+ # person.reset_name_to_default!
133
+ # person.name # => 'Gemma'
134
+ def attribute_method_affix(*affixes)
135
+ attribute_method_matchers.concat(affixes.map { |affix| AttributeMethodMatcher.new :prefix => affix[:prefix], :suffix => affix[:suffix] })
136
+ undefine_attribute_methods
137
+ end
138
+
139
+ def alias_attribute(new_name, old_name)
140
+ attribute_method_matchers.each do |matcher|
141
+ module_eval <<-STR, __FILE__, __LINE__+1
142
+ def #{matcher.method_name(new_name)}(*args)
143
+ send(:#{matcher.method_name(old_name)}, *args)
144
+ end
145
+ STR
146
+ end
147
+ end
148
+
149
+ def define_attribute_methods(attr_names)
150
+ return if attribute_methods_generated?
151
+ attr_names.each do |attr_name|
152
+ attribute_method_matchers.each do |matcher|
153
+ unless instance_method_already_implemented?(matcher.method_name(attr_name))
154
+ generate_method = "define_method_#{matcher.prefix}attribute#{matcher.suffix}"
155
+
156
+ if respond_to?(generate_method)
157
+ send(generate_method, attr_name)
158
+ else
159
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__+1
160
+ def #{matcher.method_name(attr_name)}(*args)
161
+ send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
162
+ end
163
+ STR
164
+ end
165
+ end
166
+ end
167
+ end
168
+ @attribute_methods_generated = true
169
+ end
170
+
171
+ def undefine_attribute_methods
172
+ generated_attribute_methods.module_eval do
173
+ instance_methods.each { |m| undef_method(m) }
174
+ end
175
+ @attribute_methods_generated = nil
176
+ end
177
+
178
+ def generated_attribute_methods #:nodoc:
179
+ @generated_attribute_methods ||= begin
180
+ mod = Module.new
181
+ include mod
182
+ mod
183
+ end
184
+ end
185
+
186
+ def attribute_methods_generated?
187
+ @attribute_methods_generated ||= nil
188
+ end
189
+
190
+ protected
191
+ def instance_method_already_implemented?(method_name)
192
+ method_defined?(method_name)
193
+ end
194
+
195
+ private
196
+ class AttributeMethodMatcher
197
+ attr_reader :prefix, :suffix
198
+
199
+ AttributeMethodMatch = Struct.new(:target, :attr_name)
200
+
201
+ def initialize(options = {})
202
+ options.symbolize_keys!
203
+ @prefix, @suffix = options[:prefix] || '', options[:suffix] || ''
204
+ @regex = /^(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})$/
205
+ end
206
+
207
+ def match(method_name)
208
+ if matchdata = @regex.match(method_name)
209
+ AttributeMethodMatch.new(method_missing_target, matchdata[2])
210
+ else
211
+ nil
212
+ end
213
+ end
214
+
215
+ def method_name(attr_name)
216
+ "#{prefix}#{attr_name}#{suffix}"
217
+ end
218
+
219
+ def method_missing_target
220
+ :"#{prefix}attribute#{suffix}"
221
+ end
222
+ end
223
+
224
+ def attribute_method_matchers #:nodoc:
225
+ read_inheritable_attribute(:attribute_method_matchers) || write_inheritable_attribute(:attribute_method_matchers, [])
226
+ end
227
+ end
228
+
229
+ # Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
230
+ # were first-class methods. So a Person class with a name attribute can use Person#name and
231
+ # Person#name= and never directly use the attributes hash -- except for multiple assigns with
232
+ # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
233
+ # the completed attribute is not +nil+ or 0.
234
+ #
235
+ # It's also possible to instantiate related objects, so a Client class belonging to the clients
236
+ # table with a +master_id+ foreign key can instantiate master through Client#master.
237
+ def method_missing(method_id, *args, &block)
238
+ method_name = method_id.to_s
239
+ if match = match_attribute_method?(method_name)
240
+ guard_private_attribute_method!(method_name, args)
241
+ return __send__(match.target, match.attr_name, *args, &block)
242
+ end
243
+ super
244
+ end
245
+
246
+ # A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
247
+ # <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
248
+ # which will all return +true+.
249
+ alias :respond_to_without_attributes? :respond_to?
250
+ def respond_to?(method, include_private_methods = false)
251
+ if super
252
+ return true
253
+ elsif !include_private_methods && super(method, true)
254
+ # If we're here then we haven't found among non-private methods
255
+ # but found among all methods. Which means that given method is private.
256
+ return false
257
+ elsif match_attribute_method?(method.to_s)
258
+ return true
259
+ end
260
+ super
261
+ end
262
+
263
+ protected
264
+ def attribute_method?(attr_name)
265
+ attributes.include?(attr_name)
266
+ end
267
+
268
+ private
269
+ # Returns a struct representing the matching attribute method.
270
+ # The struct's attributes are prefix, base and suffix.
271
+ def match_attribute_method?(method_name)
272
+ self.class.send(:attribute_method_matchers).each do |method|
273
+ if (match = method.match(method_name)) && attribute_method?(match.attr_name)
274
+ return match
275
+ end
276
+ end
277
+ nil
278
+ end
279
+
280
+ # prevent method_missing from calling private methods with #send
281
+ def guard_private_attribute_method!(method_name, args)
282
+ if self.class.private_method_defined?(method_name)
283
+ raise NoMethodError.new("Attempt to call private method", method_name, args)
284
+ end
285
+ end
286
+
287
+ def missing_attribute(attr_name, stack)
288
+ raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,91 @@
1
+ require 'active_support/callbacks'
2
+
3
+ module ActiveModel
4
+ module Callbacks
5
+ def self.extended(base)
6
+ base.class_eval do
7
+ include ActiveSupport::Callbacks
8
+ end
9
+ end
10
+
11
+ # Define callbacks similar to ActiveRecord ones. It means:
12
+ #
13
+ # * The callback chain is aborted whenever the block given to
14
+ # _run_callbacks returns false.
15
+ #
16
+ # * If a class is given to the fallback, it will search for
17
+ # before_create, around_create and after_create methods.
18
+ #
19
+ # == Usage
20
+ #
21
+ # First you need to define which callbacks your model will have:
22
+ #
23
+ # class MyModel
24
+ # define_model_callbacks :create
25
+ # end
26
+ #
27
+ # This will define three class methods: before_create, around_create,
28
+ # and after_create. They accept a symbol, a string, an object or a block.
29
+ #
30
+ # After you create a callback, you need to tell when they are executed.
31
+ # For example, you could do:
32
+ #
33
+ # def create
34
+ # _run_create_callbacks do
35
+ # super
36
+ # end
37
+ # end
38
+ #
39
+ # == Options
40
+ #
41
+ # define_model_callbacks accepts all options define_callbacks does, in
42
+ # case you want to overwrite a default. Besides that, it also accepts
43
+ # an :only option, where you can choose if you want all types (before,
44
+ # around or after) or just some:
45
+ #
46
+ # define_model_callbacks :initializer, :only => :after
47
+ #
48
+ def define_model_callbacks(*callbacks)
49
+ options = callbacks.extract_options!
50
+ options = { :terminator => "result == false", :scope => [:kind, :name] }.merge(options)
51
+
52
+ types = Array(options.delete(:only))
53
+ types = [:before, :around, :after] if types.empty?
54
+
55
+ callbacks.each do |callback|
56
+ define_callbacks(callback, options)
57
+
58
+ types.each do |type|
59
+ send(:"_define_#{type}_model_callback", self, callback)
60
+ end
61
+ end
62
+ end
63
+
64
+ def _define_before_model_callback(klass, callback) #:nodoc:
65
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
66
+ def self.before_#{callback}(*args, &block)
67
+ set_callback(:#{callback}, :before, *args, &block)
68
+ end
69
+ CALLBACK
70
+ end
71
+
72
+ def _define_around_model_callback(klass, callback) #:nodoc:
73
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
74
+ def self.around_#{callback}(*args, &block)
75
+ set_callback(:#{callback}, :around, *args, &block)
76
+ end
77
+ CALLBACK
78
+ end
79
+
80
+ def _define_after_model_callback(klass, callback) #:nodoc:
81
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__
82
+ def self.after_#{callback}(*args, &block)
83
+ options = args.extract_options!
84
+ options[:prepend] = true
85
+ options[:if] = Array(options[:if]) << "!halted && value != false"
86
+ set_callback(:#{callback}, :after, *(args << options), &block)
87
+ end
88
+ CALLBACK
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,8 @@
1
+ module ActiveModel
2
+ # Include ActiveModel::Conversion if your object "acts like an ActiveModel model".
3
+ module Conversion
4
+ def to_model
5
+ self
6
+ end
7
+ end
8
+ end