Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

iterating on model definition; still in rough form

  • Loading branch information...
commit af388ae013ed17808a518a08b4dca0b1c4bd9227 1 parent 9f8b14a
Philip (flip) Kromer authored
View
1  Gemfile
@@ -9,6 +9,7 @@ group :development do
gem 'jeweler', "~> 1.6"
gem 'rspec', "~> 2.5"
gem 'yard', "~> 0.6"
+ gem 'pry'
end
group :docs do
View
3  Guardfile
@@ -5,7 +5,8 @@
# watch(%r{notes/.+\.(md|txt)}) { "notes" }
# end
-rspec_opts = '--format progress' # or 'doc' for more verbose
+# '--format doc' for more verbose
+rspec_opts = '--format progress ' # --tag model_spec'
guard 'rspec', :version => 2, :cli => rspec_opts do
watch(%r{^spec/.+_spec\.rb$})
View
30 lib/gorillib/io/system_helpers.rb
@@ -0,0 +1,30 @@
+module Gorillib::CheckedPopen
+ module_function
+
+ def checked_popen(command, mode, fail_action, io_class=IO)
+ check_child_exit_status do
+ io_class.popen(command, mode) do |process|
+ yield(process)
+ end
+ end
+ rescue Errno::EPIPE
+ fail_action.call
+ end
+
+ # @private
+ NO_EXIT_STATUS = OpenStruct.new(:exitstatus => 0)
+
+ def check_child_exit_status
+ result = yield
+ status = $? || NO_EXIT_STATUS
+ unless [0, 172].include?(status.exitstatus)
+ raise ArgumentError, "Command exited with status '#{status.exitstatus}'"
+ end
+ result
+ end
+
+end
+
+::IO.class_eval do
+ include Gorillib::CheckedPopen
+end
View
3  lib/gorillib/model.rb
@@ -1,7 +1,4 @@
require "gorillib/metaprogramming/concern"
-require "gorillib/object/try_dup"
-
-require "gorillib/model/attributes"
module Gorillib
#
View
1  lib/gorillib/model/README.md
@@ -13,7 +13,6 @@
A model class has fields
A model instance has attributes that correspond to those fields
-
`.receive` .new, #receive!
`#receive!` `receive_attribute` on each attribute in the hash; `receive_remaining` on the leftovers
`#receive_attribute` type converts val, calls `write_attribute`
View
5 lib/gorillib/model/TODO.md
@@ -0,0 +1,5 @@
+
+
+* figure out the method structure for
+ - read/write/unset of attributes when Hash vs Accessors vs Instance Variables
+ - reader/writer: raw vs. hooks, dirty, etc.
View
68 lib/gorillib/model/active_model_conversion.rb
@@ -0,0 +1,68 @@
+module Gorillib::Model
+ # == Active Model Conversions
+ #
+ # Handles default conversions: to_model, to_key, to_param, and to_partial_path.
+ #
+ # Let's take for example this non-persisted object.
+ #
+ # class ContactMessage
+ # include ActiveModel::Conversion
+ #
+ # # ContactMessage are never persisted in the DB
+ # def persisted?
+ # false
+ # end
+ # end
+ #
+ # cm = ContactMessage.new
+ # cm.to_model == self # => true
+ # cm.to_key # => nil
+ # cm.to_param # => nil
+ # cm.to_path # => "contact_messages/contact_message"
+ #
+ module Conversion
+ extend Gorillib::Concern
+
+ # If your object is already designed to implement all of the Active Model
+ # you can use the default <tt>:to_model</tt> implementation, which simply
+ # returns self.
+ #
+ # If your model does not act like an Active Model object, then you should
+ # define <tt>:to_model</tt> yourself returning a proxy object that wraps
+ # your object with Active Model compliant methods.
+ def to_model
+ self
+ end
+
+ # Returns an Enumerable of all key attributes if any is set, regardless
+ # if the object is persisted or not.
+ #
+ # Note the default implementation uses persisted? just because all objects
+ # in Ruby 1.8.x responds to <tt>:id</tt>.
+ def to_key
+ persisted? ? [id] : nil
+ end
+
+ # Returns a string representing the object's key suitable for use in URLs,
+ # or nil if <tt>persisted?</tt> is false.
+ def to_param
+ persisted? ? to_key.join('-') : nil
+ end
+
+ # Returns a string identifying the path associated with the object.
+ # ActionPack uses this to find a suitable partial to represent the object.
+ def to_partial_path
+ self.class._to_partial_path
+ end
+
+ module ClassMethods #:nodoc:
+ # Provide a class level cache for the to_path. This is an
+ # internal method and should not be accessed directly.
+ def _to_partial_path #:nodoc:
+ @_to_partial_path ||= begin
+ "#{model_name.collection}/#{model_name.element}".freeze
+ end
+ end
+ end
+ end
+end
View
87 lib/gorillib/model/active_model_naming.rb
@@ -0,0 +1,87 @@
+module Gorillib::Model
+ class Name < String
+ attr_accessor :namespace
+ attr_reader :singular, :element, :collection, :partial_path, :param_key, :i18n_key
+
+ alias_method :cache_key, :collection
+
+ class_attribute :inflector
+ self.inflector ||= Gorillib::String::Inflector
+
+ def initialize(klass, namespace = nil, name = nil)
+ name ||= klass.name
+ raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank?
+
+ super(name)
+
+ @klass = klass
+ @singular = _singularize(self).freeze
+ @element = inflector.underscore(inflector.demodulize(self)).freeze
+ @human = inflector.humanize(@element).freeze
+ @i18n_key = inflector.underscore(self).to_sym
+ #
+ self.namespace = namespace
+ self.plural = inflector.pluralize(@singular)
+ end
+
+ def plural=(str)
+ @plural = str.dup.freeze
+ @collection = inflector.underscore(@plural).freeze
+ @partial_path = "#{@collection}/#{@element}".freeze
+ str
+ end
+
+ def namespace=(ns)
+ if ns.present?
+ @unnamespaced = self.sub(/^#{ns.name}::/, '')
+ @param_key = _singularize(@unnamespaced).freeze
+ else
+ @unnamespaced = nil
+ @param_key = @singular.freeze
+ end
+ end
+
+ def singular_route_key
+ inflector.singularize(route_key).freeze
+ end
+
+ def route_key
+ rk = (namespace ? inflector.pluralize(param_key) : plural.dup)
+ rk << "_index" if plural == singular
+ rk.freeze
+ end
+
+ private
+
+ def _singularize(string, replacement='_')
+ inflector.underscore(string).tr('/', replacement)
+ end
+ end
+
+ # == Active Model Naming
+ #
+ # Creates a +model_name+ method on your object.
+ #
+ # To implement, just extend ActiveModel::Naming in your object:
+ #
+ # class BookCover
+ # extend ActiveModel::Naming
+ # end
+ #
+ # BookCover.model_name # => "BookCover"
+ # BookCover.model_name.human # => "Book cover"
+ #
+ # BookCover.model_name.i18n_key # => :book_cover
+ # BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
+ #
+ # Providing the functionality that ActiveModel::Naming provides in your object
+ # is required to pass the Active Model Lint test. So either extending the provided
+ # method below, or rolling your own is required.
+ module Naming
+ # Returns a Name object for module, which can be used to retrieve all kinds
+ # of naming-related information.
+ def model_name
+ @_model_name ||= Gorillib::Model::Name.new(self, namespace)
+ end
+ end
+end
View
13 lib/gorillib/model/active_model_shim.rb
@@ -13,14 +13,23 @@ module Model
module ActiveModelShim
extend Gorillib::Concern
extend ActiveModel::Naming
- include ActiveModel::Conversion
+ include Gorillib::Model::Conversion
include ActiveModel::Validations
# @return [false]
def persisted?
false
end
- end
+
+ # Overrides ActiveModel::AttributeMethods
+ # @private
+ def attribute_method?(attr_name)
+ self.class.has_field?(attr_name)
+ end
+
+ module ClassMethods
+ end # ActiveModelShim::ClassMethods
+ end # ActiveModelShim
end
end
View
7 lib/gorillib/model/dsl_field.rb
@@ -0,0 +1,7 @@
+module Gorillib
+
+ module Model
+ module Configurable
+ end
+ end
+end
View
28 lib/gorillib/model/field.rb
@@ -23,7 +23,7 @@ def initialize(name, type, model, hsh={})
@model = model
@name = name.to_sym
@type = type
- @options = Mash.new
+ @options = {}
receive!(hsh)
end
@@ -33,6 +33,22 @@ def receive!(hsh)
@default = @options[:default]
end
+ def doc
+ @options[:doc] || "#{name} attribute"
+ end
+
+ INSCRIBED_METHOD_TYPES = [:read, :write, :unset]
+
+ def visibility(meth_type)
+ raise ArgumentError, "method type must be one of #{INSCRIBED_METHOD_TYPES.join(', ')}" unless INSCRIBED_METHOD_TYPES.include?(meth_type)
+ case @options[meth_type]
+ when true then :public
+ when nil then :public
+ when false then :none
+ else @options[meth_type]
+ end
+ end
+
# Compare field definitions
#
# @example
@@ -52,6 +68,10 @@ def [](key)
@options[key.to_sym]
end
+ def to_hash
+ @options
+ end
+
# The field name
# @return [String] the field name
def to_s
@@ -74,8 +94,8 @@ def to_sym
#
# @since 0.6.0
def inspect
- args = [name.inspect, type.to_s, to_hash.map{|key, val| "#{key.inspect} => #{val.inspect}" }.sort]
- "field #{args.join(", ")}"
+ args = [name.inspect, type.to_s, to_hash.map{|key, val| "#{key.inspect} => #{val.inspect}" }].reject(&:blank?)
+ "field(#{args.join(", ")})"
end
protected
@@ -85,7 +105,7 @@ def inspect
module Valid
VALID_NAME_RE = /\A[A-Za-z_][A-Za-z0-9_]+\z/
- def validate_name!(name)
+ def self.validate_name!(name)
raise TypeError, "can't convert #{name.class} into Symbol" unless name.respond_to? :to_sym
raise ArgumentError, "Name must start with [A-Za-z_] and subsequently contain only [A-Za-z0-9_]" unless name =~ VALID_NAME_RE
end
View
68 lib/gorillib/model/named_type.rb
@@ -0,0 +1,68 @@
+module Meta
+ module Schema
+
+ #
+ # Provides
+ #
+ module NamedSchema
+
+ #
+ # Returns the metamodel -- a module extending the type, on which all the
+ # model methods are inscribed. This allows you to override the model methods
+ # and call +super()+ to get the generic behavior.
+ #
+ # The metamodel is named for the including class, but with 'Meta::'
+ # prepended and 'Type' appended -- so Geo::Place has metamodel
+ # "Meta::Geo::PlaceType"
+ #
+ def metamodel
+ return @metamodel if @metamodel
+ @metamodel = Meta::Schema::NamedSchema.get_nested_module("Meta::#{self.name}Type")
+ self.class_eval{ include(@metamodel) }
+ @metamodel
+ end
+
+ protected
+
+ ALLOWED_VISIBILITIES = [:public, :private, :protected].freeze
+
+ # OPTIMIZE: apparently `define_method(:foo){ ... }` is slower than `def foo() ... end`
+ def define_metamodel_method(method_name, visibility=:public, &block)
+ return if visibility == :none || visibility == false
+ raise ArgumentError, "Visibility must be one of #{ ALLOWED_VISIBILITIES.join(', ') }, got '#{ visibility }'" unless ALLOWED_VISIBILITIES.include?(visibility)
+ instance_method_already_implemented?(method_name)
+ metamodel.module_eval{ define_method(method_name, &block) }
+ metamodel.module_eval "#{visibility} :#{method_name}", __FILE__, __LINE__
+ end
+
+ # These methods are deprecated on the Object class and so can be safely overridden
+ DEPRECATED_OBJECT_METHODS = %w[ id type ]
+
+ # Overrides ActiveModel::AttributeMethods
+ # @private
+ def instance_method_already_implemented?(method_name)
+ deprecated_object_method = DEPRECATED_OBJECT_METHODS.include?(method_name.to_s)
+ already_implemented = (not deprecated_object_method) && self.allocate.respond_to?(method_name, true)
+ raise ::Gorillib::Model::DangerousFieldError, "A field named '#{method_name}' would conflict with an existing method" if already_implemented
+ false
+ end
+
+ # Returns a module for the given names, rooted at Object (so
+ # implicity with '::').
+ # @example
+ # get_nested_module(["This", "That", "TheOther"])
+ # # This::That::TheOther
+ def self.get_nested_module(name)
+ name.split('::').inject(Object) do |parent_module, module_name|
+ # inherit = false makes these methods be scoped to parent_module instead of universally
+ if parent_module.const_defined?(module_name, false)
+ parent_module.const_get(module_name, false)
+ else
+ parent_module.const_set(module_name.to_sym, Module.new)
+ end
+ end
+ end
+
+ end
+ end
+end
View
207 lib/gorillib/model/record_type.rb
@@ -1,5 +1,5 @@
-module Gorillib
- module RecordType
+module Meta
+ module Type
# Provides a set of class methods for defining a field schema and instance
# methods for reading and writing attributes.
@@ -7,31 +7,18 @@ module RecordType
# @example Usage
# class Person
# include Gorillib::Meta::RecordType
- # field :name, String, :doc => 'Full name of person'
+ #
+ # field :name, String, :doc => 'Full name of person'
+ # field :height, Float, :doc => 'Height in meters'
# end
#
- # person = Person.new
+ # person = Person.new
# person.name = "Bob Dobbs, Jr"
+ # puts person #=> #<Person name="Bob Dobbs, Jr">
#
- module Attributes
- extend ActiveSupport::Concern
+ module RecordType
- # Two records are equal if they have the same class and their attributes
- # are equal.
- #
- # @example Compare for equality.
- # model == other
- #
- # @param [ActiveAttr::Attributes, Object] other The other model to compare
- #
- # @return [true, false] True if attributes are equal and other is instance
- # of the same Class, false if not.
- #
- # @since 0.2.0
- def ==(other)
- return false unless other.instance_of? self.class
- attributes == other.attributes
- end
+ extend Gorillib::Concern
# Returns a Hash of all attributes
#
@@ -40,82 +27,101 @@ def ==(other)
#
# @return [Hash{Symbol => Object}] The Hash of all attributes
#
- def attributes
- Hash[ self.class.field_names.map{|key| [key, read_attribute(key)] } ]
- end
- alias_method :to_hash, :attributes
-
- # Returns the class name plus its attributes
- #
- # @return [String] Human-readable presentation of the attributes
+ # FIXME: should this include unset
#
- def inspect
- str = "#<" << self.class.name
- str << " " unless attribute_descriptions.empty?
- str << attributes.map{|attr, val| "#{attr}: #{value.inspect}" }.join(", ")
- str << ">"
- str
+ def attributes
+ self.class.field_names.inject({}) do |hsh, fn|
+ hsh[fn] = read_attribute(fn) if attribute_set?(fn) ; hsh
+ end
end
# Read a value from the model's attributes.
#
- # @example Read an attribute with read_attribute
+ # @example Reading an attribute
# person.read_attribute(:name)
- # @example Read an attribute with bracket syntax
- # person[:name]
#
- # @param [String, Symbol, #to_s] name The name of the attribute to get.
+ # @param [String, Symbol, #to_s] fn The name of the attribute to get.
#
# @return [Object] The value of the attribute.
#
# @raise [UnknownAttributeError] if the attribute is unknown
#
- def read_attribute(name)
- if self.class.has_field?(name)
- send(name.to_s)
+ def read_attribute(fn)
+ if self.class.has_field?(fn)
+ send(fn.to_s)
else
- raise UnknownFieldError, "unknown field: #{name}"
+ raise UnknownFieldError, "unknown field: #{fn}"
end
end
- alias_method :[], :read_attribute
- # Write the value of a single attribute
+ # Write the value of a single attribute.
#
- # @example Write the attribute with write_attribute
+ # @example Writing an attribute
# person.write_attribute(:name, "Benjamin")
- # @example Write an attribute with bracket syntax
- # person[:name] = "Benjamin"
#
- # @param [String, Symbol, #to_s] name The name of the attribute to update.
+ # @param [String, Symbol, #to_s] fn The fn of the attribute to update.
# @param [Object] value The value to set for the attribute.
#
# @raise [UnknownAttributeError] if the attribute is unknown
#
- # @since 0.2.0
- def write_attribute(name, value)
- if self.class.has_field?(name)
- send("#{name}=", value)
+ def write_attribute(fn, value)
+ if self.class.has_field?(fn)
+ send("#{fn}=", value)
else
- raise UnknownAttributeError, "unknown attribute: #{name}"
+ raise UnknownAttributeError, "unknown attribute: #{fn}"
end
end
- alias_method :[]=, :write_attribute
- protected
+ def unset_attribute(fn)
+ write_attribute(fn, nil)
+ end
- # Overrides ActiveModel::AttributeMethods
- # @private
- def attribute_method?(attr_name)
- self.class.has_field?(attr_name)
+ def attribute_set?
+ true
end
- module ClassMethods
- included do
- class_attribute :fields unless self.respond_to?(:fields)
- self.fields ||= Hash.new
+ def attribute_default(fn)
+ val = case field.default
+ case val
+ when nil then nil
+ when Proc then (val.arity == 0) ? instance_exec(&val) : val.call(self, fn)
+ else field.default
end
+ end
- # Defines an field
+ # Two records are equal if they have the same class and their attributes
+ # are equal.
+ #
+ # @example Compare for equality.
+ # model == other
+ #
+ # @param [ActiveAttr::Attributes, Object] other The other model to compare
+ #
+ # @return [true, false] True if attributes are equal and other is instance
+ # of the same Class, false if not.
+ #
+ def ==(other)
+ return false unless other.instance_of?(self.class)
+ attributes == other.attributes
+ end
+
+ # Returns the class name plus its attributes
+ #
+ # @return [String] Human-readable presentation of the attributes
+ #
+ def inspect
+ str = "#<" << self.class.name
+ str << " " unless attribute_descriptions.empty?
+ str << attributes.map{|attr, val| "#{attr}: #{value.inspect}" }.join(", ")
+ str << ">"
+ str
+ end
+
+ protected
+
+ module ClassMethods
+
+ # Defines a new field
#
# For each field that is defined, a getter and setter will be added as
# an instance method to the model. An Field instance will be added to
@@ -124,21 +130,33 @@ module ClassMethods
# @example
# field :height, Integer
#
- # @param [Symbol] name -- The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]`
+ # @param [Symbol] fn -- The field name. Must start with `[A-Za-z_]` and subsequently contain only `[A-Za-z0-9_]`
# @param [Class] type -- The field's type (required)
# @option options [String] doc -- Documentation string for the field (optional)
# @option options [Proc, Object] default -- Default value, or proc that instance can evaluate to find default value
#
- # @raise [DangerousAttributeError] if the attribute name conflicts with
+ # @macro [attach] property
+ # @return [$2] the $1 property ($3)
+ #
+ # @raise [DangerousAttributeError] if the field name conflicts with
# existing methods
#
- def field(name, type, options={})
- field_def = Gorillib::Model::Field.new(name, type, options)
- fields[field_def.name] = field_def
+ def field(fn, type, options={})
+ field_def = ::Gorillib::Model::Field.new(fn, type, options)
+ @_fields[field_def.name] = field_def
define_field_methods(field_def)
field_def
end
+ def fields
+ fields = {}
+ self.ancestors.reverse.each do |ancestor|
+ next unless ancestor.instance_variable_defined?('@_fields')
+ fields.merge! ancestor.instance_variable_get('@_fields')
+ end
+ fields
+ end
+
# Array of field names as Symbols
#
# @return [Array<String>] The attribute names
@@ -154,53 +172,28 @@ def inspect
"#{self.name}[#{ field_names.join(", ") }]"
end
- # def meta_module
- # "Meta::Type::#{self.name}Type"
- # end
- #
- # def meta_module_method(name, &block)
- # end
- #
- # def define_field_methods(field)
- # meta_module_method("receive_#{field}") do
- #
- # end
- # meta_module.module_eval{ attr_accessor(field.name) }
- # end
-
- # def add_field_accessor(field_name, schema)
- # visibility = schema[:accessor] || :public
- # reader_meth = field_name ; writer_meth = "#{field_name}=" ; attr_name = "@#{field_name}"
- # unless (visibility == :none)
- # define_metamodel_method(reader_meth, visibility){ instance_variable_get(attr_name) }
- # define_metamodel_method(writer_meth, visibility){|v| instance_variable_set(attr_name, v) }
- # end
- # end
-
protected
- # Methods deprecated on the Object class which can be safely overridden
- DEPRECATED_OBJECT_METHODS = %w[ id type ]
-
- # Overrides ActiveModel::AttributeMethods
- # @private
- def instance_method_already_implemented?(method_name)
- deprecated_object_method = DEPRECATED_OBJECT_METHODS.include?(method_name.to_s)
- already_implemented = !deprecated_object_method && self.allocate.respond_to?(method_name, true)
- raise DangerousAttributeError, %Q{An attribute method named "#{method_name}" would conflict with an existing method} if already_implemented
- false
+ def define_field_methods(field)
+ ivar_name = "@#{field.name}"
+ define_metamodel_method(field.name, field.visibility(:read )){ instance_variable_get(ivar_name) }
+ define_metamodel_method("#{field.name}=", field.visibility(:write)){|v| instance_variable_set(ivar_name, v) }
+ define_metamodel_method("unset_#{field.name}", field.visibility(:unset)){ remove_instance_variable(ivar_name) }
end
private
# assign fields to subclasses
- #
- # FIXME: can't add fields to superclass after subclass was made
def inherited(subclass)
super
- subclass.fields = fields.dup
+ subclass.instance_variable_set('@_fields', {})
end
end
+
+ included do
+ extend Meta::Schema::NamedSchema
+ @_fields = {}
+ end
end
end
2  notes
@@ -1 +1 @@
-Subproject commit 6a208d4ee012bb1d2f35334baaff3de010661bbc
+Subproject commit f316c9c4c3206388976f9a06275626876e730633
View
83 spec/gorillib/model/record_type_spec.rb
@@ -0,0 +1,83 @@
+require File.expand_path('../../spec_helper', File.dirname(__FILE__))
+#
+require 'gorillib/metaprogramming/concern'
+require 'gorillib/metaprogramming/remove_method'
+require "gorillib/object/try_dup"
+#
+require 'gorillib/model/errors'
+require 'gorillib/model/field'
+require 'gorillib/model/named_type'
+require 'gorillib/model/record_type'
+#
+require 'model_test_helpers'
+
+require 'pry'
+
+describe Gorillib::Model, :model_spec => true do
+ context '.field' do
+ end
+
+ context '.fields' do
+ it 'has no fields by default' do
+ poppa_smurf.fields.should == {}
+ end
+
+ it 'inherits parent fields' do
+ poppa_smurf.field :height, Integer
+ smurfette.field :pulchritude, Float
+ poppa_smurf.fields.keys.should == [ :height ]
+ smurfette.fields.keys.should == [ :height, :pulchritude ]
+ end
+
+ it 'raises an error if a field is redefined' do
+ poppa_smurf.field :height, Integer
+ poppa_smurf.send(:define_method, :boogie){ 'na na na' }
+ ->{ smurfette.field :height, Float }.should raise_error(::Gorillib::Model::DangerousFieldError, /A field named 'height'.*conflict/)
+ ->{ smurfette.field :boogie, Float }.should raise_error(::Gorillib::Model::DangerousFieldError, /A field named 'boogie'.*conflict/)
+ end
+ end
+
+end
+
+describe Meta::Schema::NamedSchema, :model_spec => true do
+
+ context '.metamodel' do
+ it 'defines a new module named Meta::[KlassName]Type' do
+ defined?(::Meta::Gorillib::Scratch::PoppaSmurfType).should be_false
+ poppa_smurf.metamodel.should == ::Meta::Gorillib::Scratch::PoppaSmurfType
+ end
+ it 'includes metamodule in host class' do
+ poppa_smurf.metamodel
+ poppa_smurf.should < ::Meta::Gorillib::Scratch::PoppaSmurfType
+ end
+ end
+
+ context '#define_metamodel_method' do
+ before{ Meta::Schema::NamedSchema.send(:public, :define_metamodel_method) }
+
+ it 'adds method to metamodel' do
+ poppa_smurf.define_metamodel_method(:smurf){ 'smurfy!' }
+ poppa_smurf.public_instance_methods.should include(:smurf)
+ poppa_smurf.metamodel.public_instance_methods.should include(:smurf)
+ poppa_smurf.new.smurf.should == 'smurfy!'
+ end
+
+ context 'visibility' do
+ it 'raises an error if an illegal visibility is given' do
+ poppa_smurf.define_metamodel_method(:smurf, :public){ 'public' }
+ poppa_smurf.public_instance_methods.should include(:smurf)
+ end
+ it 'raises an error if an illegal visibility is given' do
+ poppa_smurf.define_metamodel_method(:smurf, :private){ 'private' }
+ poppa_smurf.private_instance_methods.should include(:smurf)
+ end
+ it 'raises an error if an illegal visibility is given' do
+ poppa_smurf.define_metamodel_method(:smurf, :protected){ 'protected' }
+ poppa_smurf.protected_instance_methods.should include(:smurf)
+ end
+ it 'raises an error if an illegal visibility is given' do
+ ->{ poppa_smurf.define_metamodel_method(:smurf, :smurfily){ } }.should raise_error(ArgumentError, /^Visibility must be.*'smurfily'/)
+ end
+ end
+ end
+end
View
2  spec/gorillib/model_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../spec_helper', File.dirname(__FILE__))
+require File.expand_path('../spec_helper', File.dirname(__FILE__))
require 'gorillib/model'
describe Gorillib::Model, :model_spec => true do
View
9 spec/gorillib/test_helpers/capture_output_spec.rb
@@ -68,13 +68,4 @@
end
- it 'makes module_functions' do
- klass = Class.new
- klass.private_method_defined?(:capture_output).should be_false
- klass.private_method_defined?(:quiet_output ).should be_false
- klass.send(:include, Gorillib::TestHelpers)
- klass.private_method_defined?(:capture_output).should be_true
- klass.private_method_defined?(:quiet_output ).should be_true
- end
-
end
View
9 spec/spec_helper.rb
@@ -11,11 +11,14 @@ def GORILLIB_ROOT_DIR *paths
# Spork.prefork do # Must restart for changes to config / code from libraries loaded here
$LOAD_PATH.unshift(GORILLIB_ROOT_DIR('lib'))
- $LOAD_PATH.unshift(GORILLIB_ROOT_DIR('spec/support'))
+$LOAD_PATH.unshift(GORILLIB_ROOT_DIR('spec/support'))
+require 'gorillib_test_helpers'
Dir[GORILLIB_ROOT_DIR('spec/support/matchers/*.rb')].each {|f| require f}
- RSpec.configure do |config|
- end
+
+RSpec.configure do |config|
+ include Gorillib::TestHelpers
+end
# end
# Spork.each_run do # This code will be run each time you run your specs.
View
5 spec/support/gorillib_test_helpers.rb
@@ -0,0 +1,5 @@
+require 'gorillib/test_helpers/capture_output'
+require 'gorillib/test_helpers/nuke_constants'
+
+module Gorillib::TestHelpers
+end
View
12 spec/support/model_test_helpers.rb
@@ -0,0 +1,12 @@
+module ::Gorillib::Scratch ; end
+module ::Meta ; module Gorillib ; module Scratch ; end ; end ; end
+
+shared_context 'model', :model_spec => true do
+ let(:poppa_smurf ){ Gorillib::Scratch::PoppaSmurf = Class.new{ include Meta::Type::RecordType } }
+ let(:smurfette ){ Gorillib::Scratch::Smurfette = Class.new(poppa_smurf) }
+
+ after do
+ ::Gorillib::Scratch.nuke_constants
+ ::Meta::Gorillib::Scratch.nuke_constants
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.