declarative-0.0.10/0000755000004100000410000000000013314503755014111 5ustar www-datawww-datadeclarative-0.0.10/declarative.gemspec0000644000004100000410000000166313314503755017747 0ustar www-datawww-datalib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'declarative/version' Gem::Specification.new do |spec| spec.name = "declarative" spec.version = Declarative::VERSION spec.authors = ["Nick Sutterer"] spec.email = ["apotonick@gmail.com"] spec.summary = %q{DSL for nested schemas.} spec.description = %q{DSL for nested generic schemas with inheritance and refining.} spec.homepage = "https://github.com/apotonick/declarative" spec.license = "MIT" spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "minitest" spec.add_development_dependency "minitest-line" end declarative-0.0.10/.travis.yml0000644000004100000410000000014513314503755016222 0ustar www-datawww-datalanguage: ruby rvm: - 2.3.1 - 1.9.3 gemfile: - Gemfile before_install: - gem install bundler declarative-0.0.10/test/0000755000004100000410000000000013314503755015070 5ustar www-datawww-datadeclarative-0.0.10/test/variables_test.rb0000644000004100000410000000337113314503755020430 0ustar www-datawww-datarequire "test_helper" class DSLOptionsTest < Minitest::Spec let(:defaults) { { id: 1, connections: { first: 1, second: 2 }, list: [3] } } Variables = Declarative::Variables after do Declarative::Inspect(defaults).must_equal %{{:id=>1, :connections=>{:first=>1, :second=>2}, :list=>[3]}} end #- Merge it "merges Merge over original" do options = Variables.merge( defaults, connections: Variables::Merge( second: 3, third: 4 ) ) options.must_equal( { id: 1, connections: { first: 1, second: 3, third: 4 }, :list=>[3] } ) end it "accepts Procs" do options = Variables.merge( defaults, connections: proc = ->(*) { raise } ) options.must_equal( { id: 1, connections: proc, :list=>[3] } ) end it "overrides original without Merge" do options = Variables.merge( defaults, connections: { second: 3, third: 4 } ) options.must_equal( { id: 1, connections: { second: 3, third: 4 }, :list=>[3] } ) end it "creates new hash if original not existent" do options = Variables.merge( defaults, bla: Variables::Merge( second: 3, third: 4 ) ) options.must_equal( {:id=>1, :connections=>{:first=>1, :second=>2}, :list=>[3], :bla=>{:second=>3, :third=>4}} ) end #- Append it "appends to Array" do options = Variables.merge( defaults, list: Variables::Append( [3, 4, 5] ) ) options.must_equal( { id: 1, connections: { first: 1, second: 2 }, :list=>[3, 3, 4, 5] } ) end it "creates new array if original not existent" do options = Variables.merge( defaults, another_list: Variables::Append( [3, 4, 5] ) ) options.must_equal( { id: 1, connections: { first: 1, second: 2 }, :list=>[3], :another_list=>[3, 4, 5] } ) end end declarative-0.0.10/test/definitions_test.rb0000644000004100000410000000744513314503755021001 0ustar www-datawww-datarequire "test_helper" class DefinitionsTest < Minitest::Spec NestedBuilder = ->(options) { base = options[:_base] || Declarative::Definitions.new(Declarative::Definitions::Definition) base.instance_exec(&options[:_block]) base } let (:schema) { Declarative::Definitions.new(Declarative::Definitions::Definition).extend(Declarative::Definitions::Inspect) } it "what" do # #add works with name schema.add :id # get works with symbol schema.get(:id).inspect.must_equal '#"id"}>' # get works with string schema.get("id").inspect.must_equal '#"id"}>' # #add with name and options schema.add(:id, unique: true) schema.get(:id).inspect.must_equal '#true, :name=>"id"}>' end it "overwrites old when called twice" do schema.add :id schema.add :id, cool: true schema.inspect.must_equal '{"id"=>#true, :name=>"id"}>}' end it "#add with block" do schema.add :artist, _nested_builder: NestedBuilder do add :name add :band, _nested_builder: NestedBuilder do add :location end end schema.inspect.must_equal '{"artist"=>#{"name"=>#"name"}>, "band"=>#{"location"=>#"location"}>}, :name=>"band"}>}, :name=>"artist"}>}' end it "#add with :nested instead of block" do nested_schema = Declarative::Definitions.new(Declarative::Definitions::Definition) nested_schema.extend(Declarative::Definitions::Inspect) nested_schema.add :name schema.add :artist, nested: nested_schema schema.inspect.must_equal '{"artist"=>#{"name"=>#"name"}>}, :name=>"artist"}>}' end it "#add with inherit: true and block" do schema.add :artist, cool: true, _nested_builder: NestedBuilder do add :name add :band, crazy: nil, _nested_builder: NestedBuilder do add :location end end schema.add :id, unique: true, value: 1 schema.add :artist, uncool: false, _nested_builder: NestedBuilder, inherit: true do add :band, normal: false, _nested_builder: NestedBuilder, inherit: true do add :genre end end schema.add :id, unique: false, inherit: true schema.inspect.must_equal '{"artist"=>#true, :nested=>{"name"=>#"name"}>, "band"=>#nil, :nested=>{"location"=>#"location"}>, "genre"=>#"genre"}>}, :name=>"band", :normal=>false}>}, :name=>"artist", :uncool=>false}>, "id"=>#false, :value=>1, :name=>"id"}>}' end it "#add with nested options followed by inherit: true" do schema.add :id, deserializer: options = { render: false } schema.add :id, inherit: true schema.get(:id)[:deserializer][:parse] = true options.must_equal(render: false) end end class DefinitionTest < Minitest::Spec let (:definition) { Declarative::Definitions::Definition.new(:name) } it "#merge does return deep copy" do options = { render: false } merged = definition.merge(options) definition.merge!(render: true) merged.must_equal(:name=>"name", render: false) end enddeclarative-0.0.10/test/schema_test.rb0000644000004100000410000001021413314503755017712 0ustar www-datawww-datarequire "test_helper" class SchemaTest < Minitest::Spec class Decorator extend Declarative::Schema def self.default_nested_class Decorator end end module AddLinks def self.included(includer) super includer.property(:links) end end class Concrete < Decorator defaults render_nil: true do |name| { as: name.to_s.upcase } end feature AddLinks property :artist, cool: true do property :name property :band, crazy: nil do property :location end end property :id, unique: true, value: 1 end it do Concrete.extend(Declarative::Inspect::Schema) Concrete.inspect Concrete.inspect.gsub(/\s/, "").must_equal 'Schema:{ "links"=>#true,:as=>"LINKS",:name=>"links"}>, "artist"=>#true,:as=>"ARTIST",:cool=>true,:nested=>Schema:{ "links"=>#"links"}>, "name"=>#"name"}>, "band"=>#nil,:nested=>Schema:{ "links"=>#"links"}>, "location"=>#"location"}>},:name=>"band"}>},:name=>"artist"}>, "id"=>#true,:as=>"ID",:unique=>true,:value=>1,:name=>"id"}>}'. gsub("\n", "").gsub(/\s/, "") end class InheritingConcrete < Concrete property :uuid end it do InheritingConcrete.extend(Declarative::Inspect::Schema) InheritingConcrete.inspect InheritingConcrete.inspect.gsub(/\s/, "").must_equal 'Schema:{ "links"=>#true,:as=>"LINKS",:name=>"links"}>, "artist"=>#true,:as=>"ARTIST",:cool=>true,:nested=>Schema:{ "links"=>#"links"}>, "name"=>#"name"}>, "band"=>#nil,:nested=>Schema:{ "links"=>#"links"}>, "location"=>#"location"}>},:name=>"band"}>},:name=>"artist"}>, "id"=>#true,:as=>"ID",:unique=>true,:value=>1,:name=>"id"}>, "uuid"=>#true,:as=>"UUID",:name=>"uuid"}>} '. gsub("\n", "").gsub(/\s/, "") end describe "::property still allows passing internal options" do class ConcreteWithOptions < Decorator defaults cool: true # you can pass your own _nested_builder and it will still receive correct, # defaultized options. property :artist, _nested_builder: ->(options) { OpenStruct.new(cool: options[:cool]) } end it do ConcreteWithOptions.extend(Declarative::Inspect::Schema).inspect.must_equal 'Schema: {"artist"=>#true, :nested=>#, :name=>"artist"}>}' end end describe "multiple ::defaults" do class Twin < Decorator module A; end module B; end module D; end defaults a: "a", _features: [A] do |name| { first: 1, _features: [D] } end # DISCUSS: currently, we only allow one dynamic block. defaults b: "b", _features: [B]# do |name, options| # {} #end property :id do end end it do Twin.extend(Declarative::Inspect::Schema).inspect.must_equal 'Schema: {"id"=>#"a", :b=>"b", :first=>1, :nested=>Schema: {}, :name=>"id"}>}' # :_features get merged. Twin.definitions.get(:id)[:nested].is_a? Twin::A Twin.definitions.get(:id)[:nested].is_a? Twin::B Twin.definitions.get(:id)[:nested].is_a? Twin::D end end end declarative-0.0.10/test/defaults_test.rb0000644000004100000410000001010513314503755020260 0ustar www-datawww-datarequire "test_helper" class DefaultsOptionsTest < Minitest::Spec let (:song) { Struct.new(:title, :author_name, :song_volume, :description).new("Revolution", "Some author", 20, nil) } let (:schema) { Declarative::Definitions.new(Declarative::Definitions::Definition).extend Declarative::Definitions::Inspect } let (:defaults) { Declarative::Defaults.new } describe "hash options combined with dynamic options" do it do defaults.merge!(render_nil: true) do |name| { as: name.to_s.upcase } end schema.add :title, _defaults: defaults schema.add :author_name schema.add :description, _defaults: defaults schema.inspect.must_equal '{"title"=>#true, :as=>"TITLE", :name=>"title"}>, "author_name"=>#"author_name"}>, "description"=>#true, :as=>"DESCRIPTION", :name=>"description"}>}' end end describe "with only dynamic property options" do it do defaults.merge!({}) do |name| { as: name.to_s.upcase } end schema.add :title, _defaults: defaults schema.add :author_name schema.add :description, _defaults: defaults schema.inspect.must_equal '{"title"=>#"TITLE", :name=>"title"}>, "author_name"=>#"author_name"}>, "description"=>#"DESCRIPTION", :name=>"description"}>}' end end describe "with only hashes" do it do defaults.merge!(render_nil: true) schema.add :title, _defaults: defaults schema.add :author_name schema.add :description, _defaults: defaults schema.inspect.must_equal '{"title"=>#true, :name=>"title"}>, "author_name"=>#"author_name"}>, "description"=>#true, :name=>"description"}>}' end end describe "#add options win" do it do defaults.merge!(render_nil: true) do |name| { as: name.to_s.upcase } end schema.add :title, as: "Title", _defaults: defaults schema.add :author_name schema.add :description, _defaults: defaults schema.inspect.must_equal '{"title"=>#true, :as=>"Title", :name=>"title"}>, "author_name"=>#"author_name"}>, "description"=>#true, :as=>"DESCRIPTION", :name=>"description"}>}' end end describe "multiple Defaults#merge!" do it "merges arrays automatically" do defaults.merge!(a: 1, b: 2) defaults.merge!( b: 3, _features: Declarative::Variables::Append(["A"]) ) defaults.merge!( _features: Declarative::Variables::Append(["B", "C"]) ) defaults.(nil, {}).inspect.must_equal "{:a=>1, :b=>3, :_features=>[\"A\", \"B\", \"C\"]}" end it "what" do defaults.merge!( _features: Declarative::Variables::Append(["A"]) ) do |name, options| { _features: Declarative::Variables::Append( ["B", "D"] ) } end defaults.(nil, {}).inspect.must_equal "{:_features=>[\"A\", \"B\", \"D\"]}" end end describe "deprecation" do require 'stringio' before do @old_stderr = $stderr $stderr = StringIO.new end after { $stderr = @old_stderr } it "prints deprecation twice" do defaults.merge!( _features: ["A"] ) do |name, options| { _features: ["B", "D"] } end defaults.(nil, {}).inspect.must_equal "{:_features=>[\"A\", \"B\", \"D\"]}" $stderr.string.must_equal %{[Declarative] Defaults#merge! and #call still accept arrays and automatically prepend those. This is now deprecated, you should replace `ary` with `Declarative::Variables::Append(ary)`.\n}*2 end end end declarative-0.0.10/test/test_helper.rb0000644000004100000410000000014013314503755017726 0ustar www-datawww-datarequire "minitest/autorun" require "declarative" require "declarative/testing" require "ostruct"declarative-0.0.10/test/heritage_test.rb0000644000004100000410000000273713314503755020255 0ustar www-datawww-datarequire "test_helper" class HeritageTest < Minitest::Spec P = Proc.new{}.extend(Declarative::Inspect) # #record module RepresenterA extend Declarative::Heritage::DSL # one arg. heritage.record(:representation_wrap=, true) # 2 args. heritage.record(:property, :name, enable: true) # 3 args. heritage.record(:property, :id, {}, &P) end it { RepresenterA.heritage.inspect.must_equal "[{:method=>:representation_wrap=, :args=>[true], :block=>nil}, {:method=>:property, :args=>[:name, {:enable=>true}], :block=>nil}, {:method=>:property, :args=>[:id, {}], :block=>#}]" } describe "dup of arguments" do module B extend Declarative::Heritage::DSL options = {render: true, nested: {render: false}} heritage.record(:property, :name, options, &P) options[:parse] = true options[:nested][:parse] = false end it { B.heritage.inspect.must_equal "[{:method=>:property, :args=>[:name, {:render=>true, :nested=>{:render=>false}}], :block=>#}]" } end describe "#call with block" do let (:heritage) { Declarative::Heritage.new.record(:property, :id, {}) } class CallWithBlock def self.property(name, options) @args = [name, options] end end it do heritage.(CallWithBlock) { |cfg| cfg[:args].last.merge!(_inherited: true) } CallWithBlock.instance_variable_get(:@args).must_equal [:id, {:_inherited=>true}] end end end declarative-0.0.10/README.md0000644000004100000410000000364313314503755015376 0ustar www-datawww-data# Declarative _DSL for nested schemas._ [![Gem Version](https://badge.fury.io/rb/declarative.svg)](http://badge.fury.io/rb/declarative) # Overview Declarative allows _declaring_ nested schemas. ## Installation Add this line to your application's Gemfile: ```ruby gem 'declarative' ``` ## Declarative::Schema Include this into a class or module to allow defining nested schemas using the popular `::property` DSL. Normally, an abstract base class will define essential configuration. ```ruby class Model extend Declarative::Schema def self.default_nested_class Model end end ``` Concrete schema-users simply derive from the base class. ```ruby class Song < Model property :id property :artist do property :id property :name end end ``` This won't do anything but populate the `::definitions` graph. ```ruby Song.definitions #=> ``` The nested schema will be a subclass of `Model`. ```ruby Song.definitions.get(:artist) #=> ``` ## Overriding Nested Building When declaring nested schemas, per default, Declarative will use its own `Schema::NestedBuilder` to create the nested schema composer. Override `::nested_builder` to define your own way of doing that. ```ruby class Model extend Declarative::Schema def self.default_nested_class Model end def self.nested_builder ->(options) do Class.new(Model) do class_eval &options[:_block] # executes `property :name` etc. on nested, fresh class. end end end end ``` ## Features You can automatically include modules into all nested schemas by using `::feature`. ```ruby class Model extend Declarative::Schema feature Bla ``` ## Defaults ```ruby class Model extend Declarative::Schema defaults visible: true ``` ## Copyright * Copyright (c) 2015 Nick Sutterer declarative-0.0.10/.gitignore0000644000004100000410000000016613314503755016104 0ustar www-datawww-data/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ *.bundle *.so *.o *.a mkmf.log declarative-0.0.10/Rakefile0000644000004100000410000000032113314503755015552 0ustar www-datawww-datarequire "bundler/gem_tasks" require "rake/testtask" task :default => [:test] Rake::TestTask.new(:test) do |test| test.libs << 'test' test.test_files = FileList['test/*_test.rb'] test.verbose = true end declarative-0.0.10/lib/0000755000004100000410000000000013314503755014657 5ustar www-datawww-datadeclarative-0.0.10/lib/declarative.rb0000644000004100000410000000036213314503755017470 0ustar www-datawww-datarequire "declarative/version" require "declarative/definitions" require "declarative/heritage" require "declarative/defaults" require "declarative/schema" require "declarative/deep_dup" require "declarative/variables" module Declarative end declarative-0.0.10/lib/declarative/0000755000004100000410000000000013314503755017142 5ustar www-datawww-datadeclarative-0.0.10/lib/declarative/version.rb0000644000004100000410000000005413314503755021153 0ustar www-datawww-datamodule Declarative VERSION = "0.0.10" end declarative-0.0.10/lib/declarative/schema.rb0000644000004100000410000000623213314503755020732 0ustar www-datawww-datarequire "declarative/definitions" require "declarative/defaults" require "declarative/variables" require "declarative/heritage" module Declarative # Include this to maintain inheritable, nested schemas with ::defaults and # ::feature the way we have it in Representable, Reform, and Disposable. # # The schema with its defnitions will be kept in ::definitions. # # Requirements to includer: ::default_nested_class, override building with ::nested_builder. module Schema def self.extended(extender) extender.extend DSL # ::property extender.extend Feature # ::feature extender.extend Heritage::DSL # ::heritage extender.extend Heritage::Inherited # ::included end module DSL def property(name, options={}, &block) heritage.record(:property, name, options, &block) build_definition(name, options, &block) end def defaults(options={}, &block) heritage.record(:defaults, options, &block) # Always convert arrays to Variables::Append instructions. options = options.merge( Defaults.wrap_arrays(options) ) block = wrap_arrays_from_block(block) if block_given? _defaults.merge!(options, &block) end def definitions @definitions ||= Definitions.new(definition_class) end def definition_class # TODO: test me. Definitions::Definition end private def build_definition(name, options={}, &block) default_options = {} default_options[:_base] = default_nested_class default_options[:_defaults] = _defaults default_options[:_nested_builder] = nested_builder if block # options = options.merge( Defaults.wrap_arrays(options) ) definitions.add(name, default_options.merge(options), &block) end def _defaults @defaults ||= Declarative::Defaults.new end def nested_builder NestedBuilder # default implementation. end NestedBuilder = ->(options) do Class.new(options[:_base]) do # base feature(*options[:_features]) class_eval(&options[:_block]) end end # When called, executes `block` and wraps all array values in Variables::Append. # This is the default behavior in older versions and allows to provide arrays for # default values that will be prepended. def wrap_arrays_from_block(block) ->(*args) { options = block.(*args) options.merge( Defaults.wrap_arrays( options ) ) } end end module Feature # features are registered as defaults using _features, which in turn get translated to # Class.new... { feature mod } which makes it recursive in nested schemas. def feature(*mods) mods.each do |mod| include mod register_feature(mod) end end private def register_feature(mod) heritage.record(:register_feature, mod) # this is only for inheritance between decorators and modules!!! ("horizontal and vertical") defaults.merge!( _features: Variables::Append([mod]) ) end end end end declarative-0.0.10/lib/declarative/deep_dup.rb0000644000004100000410000000044313314503755021255 0ustar www-datawww-datamodule Declarative class DeepDup def self.call(args) return Array[*dup_items(args)] if args.is_a?(Array) return ::Hash[dup_items(args)] if args.is_a?(::Hash) args end private def self.dup_items(arr) arr.to_a.collect { |v| call(v) } end end end declarative-0.0.10/lib/declarative/heritage.rb0000644000004100000410000000224113314503755021256 0ustar www-datawww-datarequire "declarative/deep_dup" module Declarative class Heritage < Array # Record inheritable assignments for replay in an inheriting class. def record(method, *args, &block) self << {method: method, args: DeepDup.(args), block: block} # DISCUSS: options.dup. end # Replay the recorded assignments on inheritor. # Accepts a block that will allow processing the arguments for every recorded statement. def call(inheritor, &block) each { |cfg| call!(inheritor, cfg, &block) } end private def call!(inheritor, cfg) yield cfg if block_given? # allow messing around with recorded arguments. inheritor.send(cfg[:method], *cfg[:args], &cfg[:block]) end module DSL def heritage @heritage ||= Heritage.new end end # To be extended into classes using Heritage. Inherits the heritage. module Inherited def inherited(subclass) super heritage.(subclass) end end # To be included into modules using Heritage. When included, inherits the heritage. module Included def included(mod) super heritage.(mod) end end end end declarative-0.0.10/lib/declarative/testing.rb0000644000004100000410000000201413314503755021141 0ustar www-datawww-datamodule Declarative def self.Inspect(obj) string = obj.inspect if obj.is_a?(Proc) elements = string.split("/") string = "#{elements.first}#{elements.last}" end string.gsub(/0x\w+/, "") end module Inspect def inspect string = super if is_a?(Proc) elements = string.split("/") string = "#{elements.first}#{elements.last}" end string.gsub(/0x\w+/, "") end module Schema def inspect definitions.extend(Definitions::Inspect) "Schema: #{definitions.inspect}" end end end module Definitions::Inspect def inspect each { |dfn| dfn.extend(Declarative::Inspect) if dfn[:nested] && dfn[:nested].is_a?(Declarative::Schema::DSL) dfn[:nested].extend(Declarative::Inspect::Schema) else dfn[:nested].extend(Declarative::Definitions::Inspect) if dfn[:nested] end } super end def get(*) super.extend(Declarative::Inspect) end end end declarative-0.0.10/lib/declarative/definitions.rb0000644000004100000410000000350513314503755022005 0ustar www-datawww-datamodule Declarative class Definitions < ::Hash class Definition def initialize(name, options={}, &block) @options = options.dup @options[:name] = name.to_s end def [](name) @options[name] end def merge!(hash) # TODO: this should return a new Definition instance. @options.merge!(hash) self end def merge(hash) # TODO: should be called #copy. DeepDup.(@options).merge(hash) end end def initialize(definition_class) @definition_class = definition_class super() end def each(&block) # TODO : test me! values.each(&block) end # #add is high-level behavior for Definitions#[]=. # reserved options: # :_features # :_defaults # :_base # :_nested_builder def add(name, options={}, &block) options = options[:_defaults].(name, options) if options[:_defaults] # FIXME: pipeline? base = options[:_base] if options.delete(:inherit) and parent_property = get(name) base = parent_property[:nested] options = parent_property.merge(options) # TODO: Definition#merge end if options[:_nested_builder] options[:nested] = build_nested( options.merge( _base: base, _name: name, _block: block, ) ) end # clean up, we don't want that stored in the Definition instance. [:_defaults, :_base, :_nested_builder, :_features].each { |key| options.delete(key) } self[name.to_s] = @definition_class.new(name, options) end def get(name) self[name.to_s] end private # Run builder to create nested schema (or twin, or representer, or whatever). def build_nested(options) options[:_nested_builder].(options) end end end declarative-0.0.10/lib/declarative/variables.rb0000644000004100000410000000213713314503755021442 0ustar www-datawww-datamodule Declarative # Implements the pattern of maintaining a hash of key/values (usually "defaults") # that are mutated several times by user and library code (override defaults). # # The Variables instance then represents the configuration data to be processed by the # using library (e.g. Representable or Trailblazer). class Variables class Proc < ::Proc end # @return Hash hash where `overrides` is merged onto `defaults` respecting Merge, Append etc. def self.merge(defaults, overrides) defaults = defaults.merge({}) # todo: use our DeepDup. # TODO: or how could we provide immutability? overrides.each do |k, v| if v.is_a?(Variables::Proc) defaults[k] = v.( defaults[k] ) else defaults[k] = v end end defaults end def self.Merge(merged_hash) Variables::Proc.new do |original| (original || {}).merge( merged_hash ) end end def self.Append(appended_array) Variables::Proc.new do |original| (original || []) + appended_array end end end # Variables end declarative-0.0.10/lib/declarative/defaults.rb0000644000004100000410000000341213314503755021276 0ustar www-datawww-datamodule Declarative # {Defaults} is a mutable DSL object that collects default directives via #merge!. # Internally, it uses {Variables} to implement the merging of defaults. class Defaults def initialize @static_options = {} @dynamic_options = ->(*) { {} } end # Set default values. Usually called in Schema::defaults. # This can be called multiple times and will "deep-merge" arrays, e.g. `_features: []`. def merge!(hash={}, &block) @static_options = Variables.merge( @static_options, handle_array_and_deprecate(hash) ) @dynamic_options = block if block_given? self end # Evaluate defaults and merge given_options into them. def call(name, given_options) # TODO: allow to receive rest of options/block in dynamic block. or, rather, test it as it was already implemented. evaluated_options = @dynamic_options.(name, given_options) options = Variables.merge( @static_options, handle_array_and_deprecate(evaluated_options) ) options = Variables.merge( options, handle_array_and_deprecate(given_options) ) # FIXME: given_options is not tested! end def handle_array_and_deprecate(variables) wrapped = Defaults.wrap_arrays(variables) warn "[Declarative] Defaults#merge! and #call still accept arrays and automatically prepend those. This is now deprecated, you should replace `ary` with `Declarative::Variables::Append(ary)`." if wrapped.any? variables.merge(wrapped) end # Wrap arrays in `variables` with Variables::Append so they get appended to existing # same-named arrays. def self.wrap_arrays(variables) Hash[ variables. find_all { |k,v| v.instance_of?(Array) }. collect { |k,v| [k, Variables::Append(v)] } ] end end end declarative-0.0.10/Gemfile0000644000004100000410000000014013314503755015377 0ustar www-datawww-datasource 'https://rubygems.org' # Specify your gem's dependencies in declarative.gemspec gemspec declarative-0.0.10/CHANGES.md0000644000004100000410000000304713314503755015507 0ustar www-datawww-data# 0.0.10 * `Defaults.merge!` will now deprecate non-wrapped `Array` values. The following code is no longer valid (but still works). defaults.merge!( list: [1,2] ) Instead, you need to wrap it in a command like `Variables::Append`. defaults.merge!( list: Declarative::Variables::Append( [1,2] ) ) The reason for this change is to allow all kinds of operations with defaults variables, such as merges, overrides, append, prepend, and so on. * Introduce `Declarative::Variables.merge` to merge two sets of variables. * `Defaults` now uses `Variables` for merge/overide operations. # 0.0.9 * Removing `uber` dependency. # 0.0.8 * When calling `Schema#defaults` (or `Defaults#merge!`) multiple times, same-named arrays will be joined instead of overridden. This fixes a common problem when merging different default settings. * Remove `Defaults#[]` and `Defaults#[]=`. This now happens via `#merge!`. # 0.0.7 * Simplify `Defaults` and remove a warning in Ruby 2.2.3. # 0.0.6 * `Heritage#call` now accepts a block that allows processing the arguments for every recorded statement before replaying them. This provides a hook to inject or change parameters, e.g. to mark a replay as an inheritance. # 0.0.5 * Introduce `Schema::build_definition` as a central entry point for building `Definition` without any heritage involved. # 0.0.4 * Restructured modules, there's always a public `DSL` module now, etc. # 0.0.3 * Internals, only. # 0.0.2 * First usable version with `Declarative::Schema` and friends. TODO: default_nested_class RM declarative-0.0.10/LICENSE.txt0000644000004100000410000000205613314503755015737 0ustar www-datawww-dataCopyright (c) 2015 Nick Sutterer MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.