Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Primary content migrated to the Gem...

  • Loading branch information...
commit 971582e2215ae899243ccc367a5fdf4be6813157 1 parent a29b3fc
@leeatchison authored
View
26 lib/active_dynamodb.rb
@@ -1,5 +1,25 @@
require "active_dynamodb/version"
+require "active_dynamodb/base"
+require "active_dynamodb/errors"
+require "active_dynamodb/dynamodb_connection"
+require "active_dynamodb/table_config"
+require "active_dynamodb/table"
+require "active_dynamodb/associations"
+require "active_dynamodb/attributes"
+require "active_dynamodb/query"
+require "active_dynamodb/relation"
+require "active_dynamodb/validators"
+require "active_dynamodb/persistence"
-module ActiveDynamoDB
- # Your code goes here...
-end
+#
+# TODOs:
+# ***- Implement inverse_of:
+# ***- Implement dependent: :destroy and :delete
+# ***- Methods defined on the base class should be available from within a Relation scope (http://guides.rubyonrails.org/active_record_querying.html#scopes, Section 13.2)
+# - Scopes
+# - related relations
+# - unsaved objects...
+# - has_many, :through=>
+# - caching
+# - has_many callbacks: before_add, after_add, before_remove, after_remove
+#
View
85 lib/active_dynamodb/associations.rb
@@ -0,0 +1,85 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Associations
+ #
+ #
+ class<<self
+ def association_list
+ @association_list||={}
+ end
+ private
+ end
+ #
+ # Used for has_many associations only...it will scan the included
+ # items, and remove any duplicate items or items that the associated
+ # related object has been removed...
+ #
+ def remove_all_stale_associations
+ self.class.association_list.each do |name,association|
+ remove_stale_associations_from_entry(association)
+ end
+ self
+ end
+ def remove_stale_associations association_name
+ entry=self.class.association_list[association_name.to_sym]
+ raise InvalidAssociation if entry.nil?
+ remove_stale_associations_from_entry(entry)
+ end
+ def remove_stale_associations_from_entry entry
+ new_list=[]
+ call_association(entry).each do |obj|
+ new_list<<obj.id
+ end
+ send "#{entry[:attribute_name]}=",new_list
+ end
+ def call_association entry
+ if entry[:type]==:multiple
+ rel=Relation.new(entry[:klass],entry[:klass].dynamodb_table.items,self,entry)
+ the_list=self.attributes[entry[:attribute_name].to_sym]
+ if the_list.nil? or the_list.size==0
+ rel=rel.where(entry[:klass].hash_key).is_null # Should always return an empty set...since everything must have a hash_key
+ else
+ rel=rel.where(entry[:klass].hash_key).in(*the_list)
+ end
+ rel
+ elsif entry[:type]==:single
+ entry[:klass].find self.attributes[entry[:attribute_name].to_sym]
+ else
+ raise InvalidAssociation
+ end
+ end
+ def assign_association entry,val
+ #
+ # In this example:
+ # class User < ActiveDynamoDB
+ # has_many :sessions, inverse_of: :user
+ # end
+ # class Session < ActiveDynamoDB
+ # belongs_to :user, inverse_of: :sessions
+ # end
+ #
+ # Then calling:
+ # session.user=val # val is a user in this case
+ # Will actually do::
+ # val.sessions.detach(session.user)
+ # session.user=val
+ # val.sessions.attach(val)
+ #
+ # (session is "self")
+ #
+ if entry[:type]==:single
+ if val.nil?
+ self.send("#{entry[:attribute_name]}=",nil)
+ else
+ val.send(entry[:inverse_of]).detach(self.send(entry[:attribute_name])) if entry[:inverse_of] and self.send(entry[:attribute_name])
+ self.send("#{entry[:attribute_name]}=",val.id)
+ val.send(entry[:inverse_of]).attach(self) if entry[:inverse_of]
+ end
+ else
+ raise InvalidAssociation
+ end
+ end
+ end
+end
View
108 lib/active_dynamodb/attributes.rb
@@ -0,0 +1,108 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Attributes Management
+ #
+ #
+ def id
+ @id
+ end
+ #
+ # Return all attributes
+ #
+ def attributes
+ @attributes||={}
+ end
+ #
+ # Assign all mass assignable attributes
+ #
+ def attributes= attrs
+ self.assign_attributes(attrs)
+ end
+ def update_attributes attrs
+ self.assign_attributes(attrs)
+ end
+ #
+ # Assign all mass assignable attributes
+ #
+ def assign_attributes(attributes, options = {})
+ return unless attributes
+ unless options[:without_protection]
+ attributes = sanitize_for_mass_assignment(attributes, options[:as] || :default)
+ end
+ attributes.each do |k, v|
+ if fields.include?(k.to_sym) or respond_to?("#{k}=")
+ send("#{k}=", v)
+ else
+ raise(UnknownAttributeError, "unknown attribute: #{k}")
+ end
+ end
+ end
+ def method_missing(id, *args)
+ #
+ # Create a Relation object when an association method is called
+ return call_association(self.class.association_list[id.to_sym]) if self.class.association_list[id.to_sym]
+ #
+ # Handle assigning belongs_to...
+ return assign_association(self.class.association_list[id[0..-2].to_sym],args[0]) if self.class.association_list[id[0..-2].to_sym]
+ #
+ # Fields...
+ fields.each do |field,options|
+ if id==field
+ return self.class.to_attr_type(field,attributes[field])
+ end
+ if id.to_s=="#{field}="
+ new_value=self.class.to_dynamodb_type field,args[0]
+ attribute_will_change!(field) unless new_value == attributes[field]
+ attributes[field]=new_value
+ return args[0]
+ end
+ end
+ super
+ end
+ #
+ #
+ # Field Management
+ #
+ #
+ def fields
+ self.class.fields
+ end
+ class << self
+ def fields
+ @fields||={}
+ end
+ def to_dynamodb_type key,value
+ return nil if value.nil?
+ ret=case self.fields[key.to_sym][:type]
+ when :string then value.to_s
+ when :symbol then value.to_s
+ when :integer then value.to_i
+ when :set_integers then value.map{|a|a.to_i}.to_set
+ when :set_strings then value.map{|a|a.to_s}.to_set
+ when :datetime then value.utc.to_s
+ else
+ raise InvalidAttributeType
+ end
+ ret
+ end
+ def to_attr_type key,value
+ raise InvalidField,"Unknown field: #{key}" if fields[key].nil?
+ return nil if value.nil?
+ ret=case self.fields[key.to_sym][:type]
+ when :string then value.to_s
+ when :symbol then value.to_sym
+ when :integer then value.to_i
+ when :set_integers then value.to_a.map{|a|a.to_i}
+ when :set_strings then value.to_a.map{|a|a.to_s}
+ when :datetime then value.to_datetime
+ else
+ raise InvalidAttributeType
+ end
+ ret
+ end
+ private
+ end
+ end
+end
View
16 lib/active_dynamodb/base.rb
@@ -0,0 +1,16 @@
+module ActiveDynamoDB
+ class Base
+ #
+ # Active Model
+ #
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+ include ActiveModel::Validations
+ include ActiveModel::MassAssignmentSecurity
+ include ActiveModel::Dirty
+ extend ActiveModel::Callbacks
+
+ define_model_callbacks :create, :update, :save, :destroy
+
+ end
+end
View
65 lib/active_dynamodb/dynamodb_connection.rb
@@ -0,0 +1,65 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Connection Management
+ #
+ #
+ class << self
+ #
+ # Names (default or configured)
+ #
+ def table_name
+ @dynamodb_table_name||=substitutions("%RailsEnvName%_%AppName%_%PluralModelName%")
+ end
+ def counter_table_name
+ @dynamodb_counter_table_name||=substitutions("%RailsEnvName%_Counter")
+ end
+ def counter_key_name
+ @dynamodb_counter_key_name||=substitutions("%AppName%_%ModelName%")
+ end
+
+ #
+ # Various connections to DynamoDB
+ #
+ def dynamodb
+ @dynamodb||=AWS::DynamoDB.new
+ end
+ def dynamodb_counter_table
+ return @dynamodb_counter_table unless @dynamodb_counter_table.nil?
+ the_table=dynamodb.tables[counter_table_name]
+ raise CantFindTable,table_name if the_table.nil?
+ the_table.load_schema
+ @dynamodb_counter_table=the_table
+ end
+ def dynamodb_table
+ return @dynamodb_table unless @dynamodb_table.nil?
+ the_table=dynamodb.tables[table_name]
+ raise CantFindTable,table_name if the_table.nil?
+ the_table.load_schema
+ @dynamodb_table=the_table
+ end
+ def counter_hash_key
+ dynamodb_counter_table.hash_key.name
+ end
+ def hash_key
+ dynamodb_table.hash_key.name
+ end
+ end
+
+ #
+ #
+ # Helpers to call class versions...
+ #
+ #
+ def dynamodb
+ self.class.dynamodb
+ end
+ def dynamodb_table
+ self.class.dynamodb_table
+ end
+ def validate_dynamodb
+ raise ConnectionNotDefined if dynamodb.nil?
+ end
+ end
+end
View
21 lib/active_dynamodb/errors.rb
@@ -0,0 +1,21 @@
+module ActiveDynamoDB
+ #
+ #
+ # Errors
+ #
+ #
+ class ConnectionNotDefined<Exception;end
+ class CantFindTable<Exception;end
+ class InvalidAttributeType<Exception;end
+ class InvalidField<Exception;end
+ class UnknownAttributeError<Exception;end
+ class InvalidCounterTable<Exception;end
+ class InvalidCounterKey<Exception;end
+ class InvalidAttribute<Exception;end
+ class CouldNotFindItemInDatabase<Exception;end
+ class InvalidRelationshipType<Exception;end
+ class InvalidAssociation<Exception;end
+ class ObjectIsNotPeristed<Exception;end
+ class TableNotBeingCreated<Exception;end
+ class TableNotBeingDeleted<Exception;end
+end
View
99 lib/active_dynamodb/persistence.rb
@@ -0,0 +1,99 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Save/Delete
+ #
+ #
+ def persisted?
+ !@id.nil?
+ end
+ def save
+ return false unless valid?
+ internal_save!
+ return self
+ rescue =>err
+ return nil
+ end
+ def save!
+ self.valid?
+ raise InvalidAttribute unless valid?
+ internal_save!
+ end
+ def internal_save!
+ run_callbacks :save do
+ if persisted?
+ run_callbacks :update do
+ self.updated_at=DateTime.now unless fields[:updated_at].nil?
+ item=dynamodb_table.items[@id]
+ raise CouldNotFindItemInDatabase unless item
+ item.attributes.update do |u|
+ self.changes.each do |key,change|
+ # u.set(key=>change[1])
+ u.set(key=>attributes[key])
+ end
+ end
+ @previously_changed = changes
+ @changed_attributes.clear
+ end
+ else
+ run_callbacks :create do
+ now=DateTime.now
+ self.created_at=now unless fields[:created_at].nil?
+ self.updated_at=now unless fields[:updated_at].nil?
+ the_id=self.class.get_next_available_id
+ save_data={}
+ save_data[self.class.hash_key]=the_id
+ self.changes.each do |key,change|
+ # save_data[key]=change[1]
+ save_data[key]=attributes[key]
+ end
+ dynamodb_table.items.create save_data
+ @id=the_id
+ @previously_changed = changes
+ @changed_attributes.clear
+ end
+ end
+ end
+ self
+ end
+ def delete
+ item=dynamodb_table.items[@id].delete
+ attributes={}
+ @id=nil
+ end
+ def destroy
+ run_callbacks :destroy do
+ # TODO: Add on destroy options in field declaration here...
+ item=dynamodb_table.items[@id].delete
+ attributes={}
+ end
+ @id=nil
+ end
+ class<<self
+ #
+ #
+ # Create aliases...
+ #
+ #
+ def create options=nil
+ obj=new options
+ obj.save
+ end
+ def create! options=nil
+ obj=new options
+ obj.save!
+ end
+ #
+ #
+ # Get a unique ID for object creation
+ #
+ #
+ def get_next_available_id
+ counter_item=dynamodb_counter_table.items[counter_key_name]
+ raise InvalidCounterKey if counter_item.nil?
+ counter_item.attributes.add({count:1},{return: :updated_new})["count"].to_i
+ end
+ end
+ end
+end
View
33 lib/active_dynamodb/query.rb
@@ -0,0 +1,33 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Query
+ #
+ #
+ class << self
+ def find key
+ key=key.to_i
+ item=dynamodb_table.items[key]
+ return nil unless item and item.exists?
+ obj=new
+ obj.load_from_item item
+ end
+ end
+ def load_from_item item
+ self.attributes={}
+ item.attributes.each do |key,value|
+ self.attributes[key.to_sym]=value
+ end
+ @id=item.attributes[self.class.hash_key].to_i
+ self
+ end
+ def initialize attr=nil
+ @id=nil
+ self.assign_attributes(attr) unless attr.nil?
+ fields.each do |field,options|
+ send("#{field}=",options[:default]) if send(field).nil? and options[:default]
+ end
+ end
+ end
+end
View
172 lib/active_dynamodb/relation.rb
@@ -0,0 +1,172 @@
+module ActiveDynamoDB
+ class Relation
+ #
+ # Filters/Relations
+ #
+ # Model.where(field: val).all
+ # Model.where(field1:val,field2:val).each
+ # Model.where(field1:val).where(field2:val).each
+ # Model.where(field: val).first
+ # Model.where(field: val).last
+ # Model.where(field).between(3,5).all
+ # Model.where(field).is_null.count
+ #
+ EndpointList=%w(each all first delete_all destroy_all count)
+ FilterList=%w(where begins_with between contains does_not_contain equals greater_than gte in is_null less_than lte not_equal_to not_null)
+ def initialize the_class,association_items,related_obj=nil,association_entry=nil
+ @the_class=the_class
+ @filters=[]
+ @association_items=association_items
+ @related_obj=related_obj
+ @association_entry=association_entry
+ end
+ def << val
+ #
+ # In this example:
+ # class User < ActiveDynamoDB
+ # has_many :sessions, inverse_of: :user
+ # end
+ # class Session < ActiveDynamoDB
+ # belongs_to :user, inverse_of: :sessions
+ # end
+ #
+ # Then calling:
+ # user.sessions<<val # val is a session in this case
+ # Will actually do (if val's to us is a single relation):
+ # user.sessions<<val
+ # val.user_id=user.sessions.related_obj.id
+ # Will actually do (if val's to us is a multiple relation):
+ # val.add_or_assign(user.id,:user)
+ # This does:
+ # if :user association in 'val' is a single:
+ # val.user_id=user.id
+ # if :user association in 'val' is a multiple:
+ # val.users_ids<<user.id
+ #
+ # (self is Relation of user)
+ #
+ ret=self.attach(val)
+ # val.send("#{@association_entry[:inverse_of]}_id=",self.related_obj.id) if @association_entry[:inverse_of]
+ raise ObjectIsNotPeristed unless self.related_obj.persisted?
+ val.send("add_or_assign",self.related_obj.id,@association_entry[:inverse_of]) if @association_entry[:inverse_of]
+ ret
+ end
+ def remove_stale_associations
+ @related_obj.remove_stale_associations @association_entry[:association_name]
+ end
+
+ def each &proc
+ result_items=follow_filters(@association_items)
+ result_items.each do |item|
+ obj=@the_class.new
+ obj.load_from_item item
+ proc.call obj
+ end
+ self
+ end
+ def count
+ result_items=follow_filters(@association_items)
+ result_items.count
+ end
+ def size
+ count
+ end
+ def all
+ ret=[]
+ self.each do |item|
+ ret<<item
+ end
+ ret
+ end
+ def delete_all
+ cnt=0
+ result_items=follow_filters(@association_items)
+ result_items.each do |item|
+ item.delete
+ cnt+=1
+ end
+ cnt
+ end
+ def destroy_all
+ cnt=0
+ result_items=follow_filters(@association_items)
+ result_items.each do |item|
+ obj=@the_class.new
+ obj.load_from_item item
+ obj.destroy
+ cnt+=1
+ end
+ cnt
+ end
+ def first
+ item=follow_filters(@association_items).first
+ return nil if item.nil?
+ obj=@the_class.new
+ obj.load_from_item item
+ obj
+ end
+ def attach obj
+ raise ObjectIsNotPeristed unless obj.persisted?
+ raise InvalidRelationshipType if @association_entry[:type]!=:multiple
+ list=@related_obj.send(@association_entry[:attribute_name])
+ list=[] if list.nil?
+ list<<obj.id
+ ret=@related_obj.send("#{@association_entry[:attribute_name]}=",list)
+ ret
+ end
+ def detach obj
+ raise InvalidRelationshipType if @association_entry[:type]!=:multiple
+ list=@related_obj.send(@association_entry[:attribute_name])
+ list=[] if list.nil?
+ list.delete obj.id
+ ret=@related_obj.send("#{@association_entry[:attribute_name]}=",list)
+ ret
+ end
+ def method_missing(id,*args)
+ if FilterList.include? id.to_s
+ @filters<<{filter:id,args:args}
+ else
+ super
+ end
+ self
+ end
+ def related_obj
+ @related_obj
+ end
+
+ private
+ def follow_filters items
+ @filters.each do |filter|
+ items=items.send(filter[:filter],*filter[:args])
+ end
+ items
+ end
+ class<<self
+ def method_missing(id, *args, &proc)
+ return scoped.send(id,*args,&proc) if Relation::FilterList.include?(id.to_s)
+ return scoped.send(id,*args,&proc) if Relation::EndpointList.include?(id.to_s)
+ super
+ end
+ def scoped
+ Relation.new self,dynamodb_table.items,self,{type: :none}
+ end
+ end
+ end
+ class Base
+ def add_or_assign obj_id,method
+ association=self.class.association_list[method]
+ raise InvalidAssociation if association.nil?
+ if association[:type]==:single
+ self.send("#{method}_id",obj_id)
+ elsif association[:type]==:multiple
+ if self.send("#{method}_ids").nil?
+ self.send("#{method}_ids",[obj_id])
+ else
+ self.send("#{method}_ids") << obj_id
+ end
+ else
+ raise InvalidRelationshipType
+ end
+ end
+ end
+end
View
129 lib/active_dynamodb/table.rb
@@ -0,0 +1,129 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Table Level Interface
+ #
+ #
+ class << self
+ #
+ # Create the table (and optionally counter_table) if they do not exist
+ #
+ def create_table options={}
+ options[:hash_key]||="Id"
+ options[:read_capacity]||=5
+ options[:write_capacity]||=5
+ options[:counter_table_read_capacity]||=5
+ options[:counter_table_write_capacity]||=5
+ options[:mode]||=:wait
+ options[:table]||=:create_if_not_exist
+ options[:counter_table]||=:create_if_not_exist
+ if options[:counter_table]==:create_if_not_exist
+ unless dynamodb.tables[counter_table_name].exists?
+ dynamodb.tables.create(counter_table_name,options[:counter_table_read_capacity],options[:counter_table_write_capacity],hash_key:{:Counter=>:string})
+ end
+ end
+ if options[:table]==:create_if_not_exist
+ unless dynamodb.tables[table_name].exists?
+ dynamodb.tables.create(table_name,options[:read_capacity],options[:write_capacity],hash_key:{options[:hash_key]=>:number})
+ end
+ end
+ if options[:mode]==:wait
+ if options[:counter_table]==:create_if_not_exist
+ the_table=dynamodb.tables[counter_table_name]
+ sleep 1 until the_table.status==:active
+ end
+ if options[:table]==:create_if_not_exist
+ the_table=dynamodb.tables[table_name]
+ sleep 1 until the_table.status==:active
+ end
+ end
+ end
+
+ #
+ # Create the table if it doesn't exist
+ #
+ def create_table_if_not_exist hash_key="Id",read_capacity=5,write_capacity=5,wait=true
+ return if dynamodb.exists?
+ create_table hash_key,read_capacity,write_capacity,wait
+ end
+ #
+ # Delete the table
+ #
+ def delete_table
+ dynamodb_table.delete
+ @dynamodb_table=nil
+ end
+ #
+ # Return the current read capacity
+ #
+ def read_capacity_units
+ dynamodb_table.read_capacity_units
+ end
+ #
+ # Return the current write capacity
+ #
+ def write_capacity_units
+ dynamodb_table.write_capacity_units
+ end
+ #
+ # Set new provisioned capacity
+ #
+ def provision_capacity read_capacity,write_capacity
+ dynamodb_table.provision_throughput read_capacity_units: read_capacity, write_capacity_units: write_capacity
+ end
+ #
+ # Return the table status
+ # One of: :active, :creating, :termating, :not_present
+ #
+ def table_status
+ dynamodb_table.status
+ rescue AWS::DynamoDB::Errors::ResourceNotFoundException
+ return :not_present
+ rescue TypeError # The AWS SDK returns this error during the termination process...
+ return :termating
+ end
+ #
+ # Check to see if the table is active (successfully created)
+ #
+ def table_active?
+ self.table_status==:active
+ end
+ #
+ # Check to see if the table is not present (never created or deleted)
+ #
+ def table_not_present?
+ self.table_status==:not_present
+ end
+ #
+ # Sleep until the table is present...returns an error if it is not being created
+ #
+ def wait_until_active
+ status=table_status
+ return if status==:active
+ raise TableNotBeingCreated if status!=:creating
+ sleep 1 until table_active?
+ end
+ #
+ # Sleep until the table has been successfully deleted...returns an error if it is not being deleted
+ def wait_until_deleted
+ status=table_status
+ return if status==:not_present
+ raise TableNotBeingDeleted if status!=:terminating
+ sleep 1 until table_not_present?
+ end
+ end
+ #
+ # Get the status of the table represented by this table instance.
+ #
+ def table_status
+ self.class.table_status
+ end
+ #
+ # Verify the table associated with this table instance is active.
+ #
+ def table_active?
+ self.class.table_active?
+ end
+ end
+end
View
108 lib/active_dynamodb/table_config.rb
@@ -0,0 +1,108 @@
+module ActiveDynamoDB
+ class Base
+ #
+ #
+ # Table Configuration
+ #
+ #
+ class << self
+ #
+ # Use an existing DynamDB connection...otherwise, it is created on first use.
+ #
+ def dynamodb= conn
+ @dynamodb=conn
+ end
+ #
+ # Name of this table in the DynamoDB service.
+ # Substitutions allowed:
+ # %RailsEnvName% - Capitalized name of the current Rails environment
+ # %ModelName% - Name of the model
+ # %AppName% - Name of the Rails App
+ # Example:
+ # dynamodb_table_name "%RailsEnvName%_%AppName%_%ModelName%"
+ #
+ def dynamodb_table_name name
+ @dynamodb_table_name=substitutions name
+ end
+ #
+ # Name of the table used for creating index counters.
+ # Specifies the name of the table, and the value of the hash_key
+ # for this table to use. This item holds an integer that is
+ # autonomously incremented on every use. The table/key pair
+ # *should* be unique across all apps and all tables used in
+ # this AWS account.
+ # Substitutions allowed:
+ # %RailsEnvName% - Capitalized name of the current Rails environment
+ # %ModelName% - Name of the model
+ # %AppName% - Name of the Rails App
+ # Example:
+ # dynamodb_counter_table_name "%RailsEnvName%_Counter","%AppName%_%ModelName%"
+ #
+ def dynamodb_counter_table_name table,key
+ @dynamodb_counter_table_name=substitutions table
+ @dynamodb_counter_key_name=substitutions key
+ end
+
+ #
+ # Configure a field for the schema
+ #
+ def field attr_key,options={}
+ options[:type]||=:string
+ fields[attr_key]=options
+ end
+ #
+ # Setup standard timestamps
+ #
+ def field_timestamps
+ field :created_at,type: :datetime
+ field :updated_at,type: :datetime
+ end
+
+ #
+ # Has Many Association
+ #
+ def has_many name,options={}
+ # TODO: Options to add: select, field_name (similar to foreign_key, but local)
+ class_name=options[:class_name]||name.to_s.singularize.capitalize
+ attribute_name="#{name}_ids".to_sym
+ association_list[name.to_sym]={
+ association_name: name,
+ attribute_name: attribute_name,
+ type: :multiple,
+ class_name: class_name,
+ klass: Kernel.const_get(class_name),
+ inverse_of: options[:inverse_of],
+ dependent: options[:dependent], # LEELEE: TODO
+ }
+ field attribute_name,type: :set_integers
+ end
+ #
+ # Belongs To Association
+ #
+ def belongs_to name,options={}
+ # TODO: Options to add: select, field_name (similar to foreign_key, but local)
+ class_name=options[:class_name]||name.to_s.singularize.capitalize
+ attribute_name="#{name}_id".to_sym
+ association_list[name.to_sym]={
+ association_name: name,
+ attribute_name: attribute_name,
+ type: :single,
+ class_name: options[:class_name]||name.to_s.singularize.capitalize,
+ klass:Kernel.const_get(class_name),
+ inverse_of: options[:inverse_of],
+ dependent: options[:dependent], # LEELEE: TODO
+ }
+ field attribute_name,type: :integer
+ end
+
+ private
+ def substitutions str
+ str.
+ gsub(/%RailsEnvName%/,Rails.env.capitalize).
+ gsub(/%ModelName%/,model_name).
+ gsub(/%PluralModelName%/,model_name.pluralize).
+ gsub(/%AppName%/,Rails.application.class.parent_name)
+ end
+ end
+ end
+end
View
25 lib/active_dynamodb/validators.rb
@@ -0,0 +1,25 @@
+module ActiveDynamoDB
+ class Base
+ #
+ # Validators
+ #
+ class UniquenessValidator < ActiveModel::EachValidator
+ def initialize(options)
+ super(options.reverse_merge(:case_sensitive => true))
+ end
+ def setup(klass)
+ @klass = klass
+ end
+ def validate_each(record, attribute, value)
+ matcher=@klass.where(attribute=>value)
+ match=matcher.where(options[:scope]=>record.send(options[:scope])) if options[:scope]
+ any_match=matcher.first
+ if any_match
+ if any_match.id!=record.id
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
+ end
+ end
+ end
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.