ninja-model 0.4.2
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/Gemfile +2 -0
- data/README.md +1 -0
- data/Rakefile +38 -0
- data/lib/generators/ninja_model/model/model_generator.rb +19 -0
- data/lib/generators/ninja_model/model/templates/model.rb +7 -0
- data/lib/generators/ninja_model/scaffold/scaffold_generator.rb +69 -0
- data/lib/generators/ninja_model.rb +12 -0
- data/lib/ninja-model.rb +1 -0
- data/lib/ninja_model/adapters/abstract_adapter.rb +63 -0
- data/lib/ninja_model/adapters/adapter_manager.rb +67 -0
- data/lib/ninja_model/adapters/adapter_pool.rb +128 -0
- data/lib/ninja_model/adapters/adapter_specification.rb +11 -0
- data/lib/ninja_model/adapters.rb +70 -0
- data/lib/ninja_model/associations/active_record_proxy.rb +53 -0
- data/lib/ninja_model/associations/association_proxy.rb +8 -0
- data/lib/ninja_model/associations/belongs_to_association.rb +31 -0
- data/lib/ninja_model/associations/has_many_association.rb +36 -0
- data/lib/ninja_model/associations/has_one_association.rb +19 -0
- data/lib/ninja_model/associations/ninja_model_proxy.rb +46 -0
- data/lib/ninja_model/associations.rb +198 -0
- data/lib/ninja_model/attributes.rb +134 -0
- data/lib/ninja_model/base.rb +106 -0
- data/lib/ninja_model/callbacks.rb +14 -0
- data/lib/ninja_model/configuration.rb +20 -0
- data/lib/ninja_model/core_ext/symbol.rb +7 -0
- data/lib/ninja_model/errors.rb +5 -0
- data/lib/ninja_model/identity.rb +24 -0
- data/lib/ninja_model/log_subscriber.rb +18 -0
- data/lib/ninja_model/persistence.rb +76 -0
- data/lib/ninja_model/predicate.rb +43 -0
- data/lib/ninja_model/railtie.rb +27 -0
- data/lib/ninja_model/reflection.rb +118 -0
- data/lib/ninja_model/relation/finder_methods.rb +74 -0
- data/lib/ninja_model/relation/query_methods.rb +62 -0
- data/lib/ninja_model/relation/spawn_methods.rb +59 -0
- data/lib/ninja_model/relation.rb +80 -0
- data/lib/ninja_model/scoping.rb +50 -0
- data/lib/ninja_model/validation.rb +38 -0
- data/lib/ninja_model/version.rb +3 -0
- data/lib/ninja_model.rb +38 -0
- data/spec/ninja_model/adapters/adapter_pool_spec.rb +72 -0
- data/spec/ninja_model/adapters_spec.rb +8 -0
- data/spec/ninja_model/attributes_spec.rb +85 -0
- data/spec/ninja_model/base_spec.rb +66 -0
- data/spec/ninja_model/identity_spec.rb +41 -0
- data/spec/ninja_model/persistence_spec.rb +9 -0
- data/spec/ninja_model/predicate_spec.rb +13 -0
- data/spec/ninja_model/query_methods_spec.rb +76 -0
- data/spec/ninja_model/relation_spec.rb +26 -0
- data/spec/ninja_model/scoping_spec.rb +40 -0
- data/spec/ninja_model_spec.rb +11 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/active_model_lint.rb +17 -0
- metadata +214 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
module Associations
|
3
|
+
class NinjaModelProxy
|
4
|
+
attr_reader :proxy_klass
|
5
|
+
def initialize(active_record)
|
6
|
+
@klass = active_record
|
7
|
+
@klass.class_eval do
|
8
|
+
def ninja_proxy
|
9
|
+
@ninja_proxy ||= begin
|
10
|
+
self.class.ninja_proxy.instance(self)
|
11
|
+
end
|
12
|
+
@ninja_proxy.attributes = self.attributes.delete_if { |k,v| k.eql?('id') }
|
13
|
+
@ninja_proxy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
@proxy_klass = active_record.parent.const_set("#{@klass.model_name}Proxy", Class.new(NinjaModel::Base))
|
18
|
+
|
19
|
+
@klass.columns_hash.each_pair do |k,v|
|
20
|
+
@proxy_klass.send :attribute, k, v.type, v.default, @proxy_klass
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def instance(obj)
|
25
|
+
proxy = @proxy_klass.new
|
26
|
+
proxy.send :instantiate, {'attributes' => obj.attributes}
|
27
|
+
proxy
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_association(macro, association_id, options)
|
31
|
+
unless macro.eql?(:belongs_to)
|
32
|
+
options = {:foreign_key => derive_foreign_key}.merge(options)
|
33
|
+
end
|
34
|
+
|
35
|
+
@proxy = nil
|
36
|
+
@proxy_klass.send macro, association_id, options
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def derive_foreign_key
|
42
|
+
"#{@klass.name.underscore}_id".to_sym
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'ninja_model/associations/active_record_proxy'
|
3
|
+
require 'ninja_model/associations/ninja_model_proxy'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Associations
|
7
|
+
module ClassMethods
|
8
|
+
alias :has_one_without_ninja_model :has_one
|
9
|
+
def has_one(association_id, options = {})
|
10
|
+
if ninja_model?(:has_one, options[:class_name] || association_id)
|
11
|
+
ninja_proxy.handle_association(:has_one, association_id, options)
|
12
|
+
else
|
13
|
+
has_one_without_ninja_model(association_id, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def ninja_proxy
|
18
|
+
read_inheritable_attribute(:ninja_proxy) || write_inheritable_attribute(:ninja_proxy, NinjaModel::Associations::NinjaModelProxy.new(self))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def ninja_model?(macro, association)
|
24
|
+
klass = association.to_s.camelize
|
25
|
+
klass = klass.singularize unless [:has_one, :belongs_to].include?(macro)
|
26
|
+
klass = klass.constantize
|
27
|
+
klass.ancestors.include?(NinjaModel::Base)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method, *args)
|
32
|
+
begin
|
33
|
+
super
|
34
|
+
rescue NoMethodError => ex
|
35
|
+
if self.class.read_inheritable_attribute(:ninja_proxy) && ninja_proxy.respond_to?(method)
|
36
|
+
ninja_proxy.send(method, *args)
|
37
|
+
else
|
38
|
+
raise ex
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Reflection
|
45
|
+
module ClassMethods
|
46
|
+
alias :reflect_on_association_without_ninja_model :reflect_on_association
|
47
|
+
def reflect_on_association(association)
|
48
|
+
if read_inheritable_attribute(:ninja_proxy) && ninja_proxy.proxy_klass.reflections.include?(association)
|
49
|
+
ninja_proxy.proxy_klass.reflect_on_association(association)
|
50
|
+
else
|
51
|
+
reflect_on_association_without_ninja_model(association)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module NinjaModel
|
59
|
+
module Associations
|
60
|
+
extend ActiveSupport::Concern
|
61
|
+
|
62
|
+
autoload :AssociationProxy, 'ninja_model/associations/association_proxy'
|
63
|
+
autoload :HasOneAssociation, 'ninja_model/associations/has_one_association'
|
64
|
+
autoload :HasManyAssociation, 'ninja_model/associations/has_many_association'
|
65
|
+
autoload :BelongsToAssociation, 'ninja_model/associations/belongs_to_association'
|
66
|
+
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
def has_one(association_id, options = {})
|
70
|
+
if ninja_model?(:has_one, options[:class_name] || association_id)
|
71
|
+
reflection = create_has_one_reflection(association_id, options)
|
72
|
+
association_accessor_methods(reflection, HasOneAssociation)
|
73
|
+
#association_constructor_method(:build, reflection, HasOneAssociation)
|
74
|
+
#association_constructor_method(:create, reflection, HasOneAssociation)
|
75
|
+
#configure_dependency_for_has_one(reflection)
|
76
|
+
else
|
77
|
+
#puts "Setting up has_one proxy for #{association_id}"
|
78
|
+
proxy.handle_association(:has_one, association_id, options)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def belongs_to(association_id, options = {})
|
83
|
+
if ninja_model?(:belongs_to, options[:class_name] || association_id)
|
84
|
+
reflection = create_belongs_to_reflection(association_id, options)
|
85
|
+
association_accessor_methods(reflection, BelongsToAssociation)
|
86
|
+
#association_constructor_method(:build, reflection, BelongsToAssociation)
|
87
|
+
#association_constructor_method(:create, reflection, BelongsToAssociation)
|
88
|
+
else
|
89
|
+
proxy.handle_association(:belongs_to, association_id, options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def has_many(association_id, options = {})
|
94
|
+
if ninja_model?(:has_many, association_id)
|
95
|
+
reflection = create_has_many_reflection(association_id, options)
|
96
|
+
collection_accessor_methods(reflection, HasManyAssociation)
|
97
|
+
#collection_accessor_methods(reflection, HasManyAssociation)
|
98
|
+
else
|
99
|
+
proxy.handle_association(:has_many, association_id, options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def proxy
|
104
|
+
read_inheritable_attribute(:proxy) || write_inheritable_attribute(:proxy, ActiveRecordProxy.new(self))
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def create_has_one_reflection(association, options = {})
|
110
|
+
create_reflection(:has_one, association, options, self)
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_has_many_reflection(association, options = {})
|
114
|
+
create_reflection(:has_many, association, options, self)
|
115
|
+
#options[:extend] = create_extension_modules(association, extension
|
116
|
+
end
|
117
|
+
|
118
|
+
def create_belongs_to_reflection(association, options = {})
|
119
|
+
create_reflection(:belongs_to, association, options, self)
|
120
|
+
end
|
121
|
+
|
122
|
+
def association_accessor_methods(reflection, association_proxy_class)
|
123
|
+
redefine_method(reflection.name) do |*params|
|
124
|
+
association = association_instance_get(reflection.name)
|
125
|
+
|
126
|
+
if association.nil?
|
127
|
+
association = association_proxy_class.new(self, reflection)
|
128
|
+
retval = association.reload
|
129
|
+
if retval.nil? and association_proxy_class == BelongsToAssociation
|
130
|
+
association_instance_set(reflection.name, nil)
|
131
|
+
return nil
|
132
|
+
end
|
133
|
+
association_instance_set(reflection.name, association)
|
134
|
+
end
|
135
|
+
association.target.nil? ? nil : association
|
136
|
+
end
|
137
|
+
|
138
|
+
redefine_method("loaded_#{reflection.name}?") do
|
139
|
+
association = association_instance_get(reflection.name)
|
140
|
+
association && association.loaded?
|
141
|
+
end
|
142
|
+
|
143
|
+
redefine_method("#{reflection.name}=") do |new_value|
|
144
|
+
association = association_instance_get(reflection.name)
|
145
|
+
if association.nil? || association.target != new_value
|
146
|
+
association = association_proxy_class.new(self, reflection)
|
147
|
+
end
|
148
|
+
|
149
|
+
association.replace(new_value)
|
150
|
+
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
151
|
+
end
|
152
|
+
|
153
|
+
redefine_method("set_#{reflection.name}_target") do |target|
|
154
|
+
return if target.nil? and association_proxy_class == BelongsToAssociation
|
155
|
+
association = association_proxy_class.new(self, reflection)
|
156
|
+
association.target = target
|
157
|
+
association_instance_set(reflection.name, association)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def collection_accessor_methods(reflection, association_proxy_class)
|
162
|
+
collection_reader_method(reflection, association_proxy_class)
|
163
|
+
end
|
164
|
+
|
165
|
+
def collection_reader_method(reflection, association_proxy_class)
|
166
|
+
redefine_method(reflection.name) do |*params|
|
167
|
+
association = association_instance_get(reflection.name)
|
168
|
+
|
169
|
+
if association.nil?
|
170
|
+
association = association_proxy_class.new(self, reflection)
|
171
|
+
end
|
172
|
+
association_instance_set(reflection.name, association)
|
173
|
+
association
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def association_instance_get(name)
|
179
|
+
ivar = "@#{name}"
|
180
|
+
if instance_variable_defined?(ivar)
|
181
|
+
association = instance_variable_get(ivar)
|
182
|
+
association if association.respond_to?(:loaded?)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def association_instance_set(name, association)
|
187
|
+
instance_variable_set("@#{name}", association)
|
188
|
+
end
|
189
|
+
|
190
|
+
def method_missing(method, *args)
|
191
|
+
if self.class.read_inheritable_attribute(:proxy) && proxy.respond_to?(method)
|
192
|
+
proxy.send(method, *args)
|
193
|
+
else
|
194
|
+
super
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_model/attribute_methods'
|
3
|
+
|
4
|
+
module NinjaModel
|
5
|
+
class Attribute
|
6
|
+
attr_reader :name, :type, :default, :primary_key
|
7
|
+
alias :primary_key? :primary_key
|
8
|
+
alias :primary :primary_key
|
9
|
+
|
10
|
+
def initialize(name, type, default, owner_class, options)
|
11
|
+
@name, @type, @default = name.to_s, type, default
|
12
|
+
@owner_class = owner_class
|
13
|
+
@options = options
|
14
|
+
@primary_key = options.key?(:primary_key) && options[:primary_key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_methods!
|
18
|
+
@owner_class.define_attribute_methods(true)
|
19
|
+
@owner_class.primary_key = name.to_sym if @primary_key
|
20
|
+
end
|
21
|
+
|
22
|
+
def convert(value)
|
23
|
+
case type
|
24
|
+
when :string then value
|
25
|
+
when :text then value
|
26
|
+
when :integer then value.to_i rescue value ? 1 : 0
|
27
|
+
when :float then value.to_f
|
28
|
+
when :date then ActiveRecord::ConnectionAdapters::Column.string_to_date(value)
|
29
|
+
else value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module Attributes
|
35
|
+
extend ActiveSupport::Concern
|
36
|
+
include ActiveModel::AttributeMethods
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def attribute(name, data_type, *args)
|
40
|
+
name = name.to_s
|
41
|
+
opts = args.extract_options!
|
42
|
+
default = args.first unless args.blank?
|
43
|
+
new_attr = Attribute.new(name, data_type, default, self, opts)
|
44
|
+
self.model_attributes << new_attr
|
45
|
+
new_attr.define_methods!
|
46
|
+
end
|
47
|
+
|
48
|
+
def define_attribute_methods(force = false)
|
49
|
+
return unless self.model_attributes
|
50
|
+
undefine_attribute_methods if force
|
51
|
+
super(self.model_attributes.map { |attr| attr.name })
|
52
|
+
end
|
53
|
+
|
54
|
+
def columns
|
55
|
+
model_attributes
|
56
|
+
end
|
57
|
+
|
58
|
+
def model_attributes_hash
|
59
|
+
@attributes_hash ||= HashWithIndifferentAccess[model_attributes.map { |attribute| [attribute.name, attribute] }]
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :columns_hash :model_attributes_hash
|
63
|
+
|
64
|
+
def attribute_names
|
65
|
+
@attribute_names ||= model_attributes.map { |attribute| attribute.name }
|
66
|
+
end
|
67
|
+
|
68
|
+
alias :column_names :attribute_names
|
69
|
+
end
|
70
|
+
|
71
|
+
included do
|
72
|
+
class_inheritable_accessor :model_attributes
|
73
|
+
self.model_attributes = []
|
74
|
+
attribute_method_suffix('', '=')
|
75
|
+
end
|
76
|
+
|
77
|
+
def attributes_from_model_attributes
|
78
|
+
self.class.model_attributes.inject({}) do |result, attr|
|
79
|
+
result[attr.name] = attr.default unless attr.name == self.class.primary_key
|
80
|
+
result
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_attribute(name, value)
|
85
|
+
name = name.to_s
|
86
|
+
if a = self.class.model_attributes_hash[name]
|
87
|
+
@attributes[name] = value
|
88
|
+
else
|
89
|
+
raise NoMethodError, "Unknown attribute #{name.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def read_attribute(name)
|
94
|
+
name = name.to_s
|
95
|
+
if !(value = @attributes[name]).nil?
|
96
|
+
self.class.model_attributes_hash[name].convert(@attributes[name])
|
97
|
+
else
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def [](attr_name)
|
103
|
+
read_attribute(attr_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def []=(attr_name, value)
|
107
|
+
write_attribute(attr_name, value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def attributes=(new_attributes)
|
111
|
+
return unless new_attributes.is_a?(Hash)
|
112
|
+
attributes = new_attributes.stringify_keys
|
113
|
+
|
114
|
+
attributes.each do |k,v|
|
115
|
+
respond_to?("#{k}=".to_sym) ? send("#{k}=".to_sym, v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def attribute_method?(name)
|
120
|
+
name = name.to_s
|
121
|
+
self.class.model_attributes_hash.key?(name)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def attribute(name)
|
127
|
+
read_attribute(name)
|
128
|
+
end
|
129
|
+
|
130
|
+
def attribute=(name, value)
|
131
|
+
write_attribute(name, value)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
class Base
|
3
|
+
include Attributes
|
4
|
+
include Callbacks
|
5
|
+
include Identity
|
6
|
+
include Persistence
|
7
|
+
include Scoping
|
8
|
+
include Validation
|
9
|
+
include Adapters
|
10
|
+
include Associations
|
11
|
+
include Reflection
|
12
|
+
include ActiveRecord::Aggregations
|
13
|
+
extend ActiveModel::Translation
|
14
|
+
extend ActiveModel::Naming
|
15
|
+
|
16
|
+
class_inheritable_accessor :default_scoping, :instance_writer => false
|
17
|
+
self.default_scoping = []
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
delegate :find, :first, :last, :all, :exists?, :to => :scoped
|
22
|
+
delegate :where, :order, :limit, :to => :scoped
|
23
|
+
|
24
|
+
def configuration_path
|
25
|
+
@config_path ||= File.join(Rails.root, "config/ninja_model.yml")
|
26
|
+
end
|
27
|
+
|
28
|
+
def configuration_path=(new_path)
|
29
|
+
@config_path = new_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def configuration
|
33
|
+
require 'erb'
|
34
|
+
require 'yaml'
|
35
|
+
@configuration ||= YAML::load(ERB.new(IO.read(configuration_path)).result).with_indifferent_access
|
36
|
+
end
|
37
|
+
|
38
|
+
def relation
|
39
|
+
@relation ||= Relation.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def logger
|
43
|
+
::NinjaModel.logger
|
44
|
+
end
|
45
|
+
|
46
|
+
def unscoped
|
47
|
+
block_given? ? relation.scoping { yield } : relation
|
48
|
+
end
|
49
|
+
|
50
|
+
def scoped_methods
|
51
|
+
key = "#{self}_scoped_methods".to_sym
|
52
|
+
Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_scope(options = {})
|
56
|
+
reset_scoped_methods
|
57
|
+
self.default_scoping << build_finder_relation(options, default_scoping.pop)
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_scoped_methods
|
61
|
+
last = scoped_methods.last
|
62
|
+
last.is_a?(Proc) ? unscoped(&last) : last
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset_scoped_methods
|
66
|
+
Thread.current["#{self}_scoped_methods".to_sym] = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def build_finder_relation(options = {}, scope = nil)
|
72
|
+
relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
|
73
|
+
relation = scope.merge(relation) if scope
|
74
|
+
relation
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def attributes
|
79
|
+
attrs = {}
|
80
|
+
self.class.attribute_names.each { |name|
|
81
|
+
attrs[name] = read_attribute(name)
|
82
|
+
}
|
83
|
+
attrs
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize(attributes = nil)
|
87
|
+
@attributes = attributes_from_model_attributes
|
88
|
+
self.attributes = attributes unless attributes.nil?
|
89
|
+
@persisted = false
|
90
|
+
@readonly = true
|
91
|
+
@destroyed = false
|
92
|
+
result = yield self if block_given?
|
93
|
+
_run_initialize_callbacks
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
def instantiate(record)
|
98
|
+
@attributes = record.stringify_keys
|
99
|
+
@readonly = @destroyed = false
|
100
|
+
@persisted = true
|
101
|
+
self
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
require 'ninja_model/core_ext/symbol'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_model/callbacks'
|
3
|
+
|
4
|
+
module NinjaModel
|
5
|
+
module Callbacks
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
extend ActiveModel::Callbacks
|
10
|
+
define_model_callbacks :initialize, :find, :touch, :only => :after
|
11
|
+
define_model_callbacks :save, :create, :update, :destroy, :validation
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
|
3
|
+
mattr_accessor :configuration
|
4
|
+
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :config_file_path, :adapter_path, :specs
|
7
|
+
|
8
|
+
def self.create
|
9
|
+
NinjaModel.configuration ||= new
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@config_file_path = 'config/ninja_model.yml'
|
16
|
+
@adapter_path = 'ninja_model/adapters'
|
17
|
+
@specs = {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module NinjaModel
|
4
|
+
module Identity
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::AttributeMethods
|
7
|
+
|
8
|
+
included do
|
9
|
+
class_inheritable_accessor :primary_key
|
10
|
+
self.primary_key = :id
|
11
|
+
undef_method(:id) if method_defined?(:id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_param
|
15
|
+
send(self.class.primary_key).to_s if persisted?
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_key
|
19
|
+
key = nil
|
20
|
+
key = send(self.class.primary_key) if persisted?
|
21
|
+
[key] if key
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
|
+
def xml(event)
|
4
|
+
return unless logger.debug?
|
5
|
+
|
6
|
+
name = '%s (%.1fms)' % [event.payload[:name], event.duration]
|
7
|
+
xml = event.payload[:xml]
|
8
|
+
|
9
|
+
debug " #{name} #{xml}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def logger
|
13
|
+
NinjaModel::Base.logger
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
ActiveRecord::LogSubscriber.attach_to :ninja_model
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module NinjaModel
|
4
|
+
module Persistence
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::AttributeMethods
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def get(rel)
|
10
|
+
end
|
11
|
+
|
12
|
+
def persist_with(adapter)
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
class_inheritable_accessor :persistence_adapter
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(*)
|
22
|
+
run_callbacks :save do
|
23
|
+
create_or_update
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_or_update
|
28
|
+
if new_record?
|
29
|
+
create
|
30
|
+
else
|
31
|
+
update
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create
|
36
|
+
run_callbacks :create do
|
37
|
+
if self.class.adapter.create(self)
|
38
|
+
@persisted = true
|
39
|
+
end
|
40
|
+
@persisted
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def update
|
45
|
+
self.class.adapter.update(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def new_record?
|
49
|
+
!@persisted
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroyed?
|
53
|
+
@destroyed
|
54
|
+
end
|
55
|
+
|
56
|
+
def persisted?
|
57
|
+
@persisted && !destroyed?
|
58
|
+
end
|
59
|
+
|
60
|
+
def destroy
|
61
|
+
if self.class.adapter.destroy(self)
|
62
|
+
@destroyed = true
|
63
|
+
end
|
64
|
+
@destroyed
|
65
|
+
end
|
66
|
+
|
67
|
+
def reload
|
68
|
+
self.class.adapter.reload(self)
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_attributes(attributes)
|
72
|
+
self.attributes = attributes
|
73
|
+
save
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module NinjaModel
|
2
|
+
class Predicate
|
3
|
+
|
4
|
+
PREDICATES = [:eq, :ne, :gt, :gte, :lt, :lte, :in]
|
5
|
+
|
6
|
+
attr_reader :attribute, :method, :value
|
7
|
+
|
8
|
+
def initialize(attribute, method, *args)
|
9
|
+
@attribute = attribute
|
10
|
+
@method = method
|
11
|
+
@valued = !args.blank?
|
12
|
+
@value = args.blank? ? nil : args.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def value=(value)
|
16
|
+
@value = value
|
17
|
+
@valued = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_value?
|
21
|
+
@valued
|
22
|
+
end
|
23
|
+
|
24
|
+
def test(suspect)
|
25
|
+
case method
|
26
|
+
when :eq
|
27
|
+
suspect.eql?(value)
|
28
|
+
when :ne
|
29
|
+
!suspect.eql?(value)
|
30
|
+
when :gt
|
31
|
+
suspect > value
|
32
|
+
when :gte
|
33
|
+
suspect >= value
|
34
|
+
when :lt
|
35
|
+
suspect < value
|
36
|
+
when :lte
|
37
|
+
suspect <= value
|
38
|
+
when :in
|
39
|
+
value.include?(suspect)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|