king_placeholder 0.0.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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gem 'king_views', :git => 'https://github.com/salesking/king_views.git', :branch => 'develop'
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Georg Leciejewski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,63 @@
1
+ = KingPlaceholder
2
+
3
+ This gem was extraced from SalesKing where it handles placeholders substitution
4
+ for user supplied email-, text- and export- templates.
5
+
6
+ Define the available methods in your class
7
+
8
+ class User
9
+ include KingPlaceholder
10
+ has_many :comments
11
+ has_placeholders :firstname
12
+ end
13
+ class Comment
14
+ include KingPlaceholder
15
+ has_placeholders :text
16
+ end
17
+
18
+ Use placeholder names in square brackets:
19
+
20
+ @user.expand_placeholders("Hello [user.first_name]")
21
+ => Hello Schorsch
22
+
23
+
24
+ It can also handle relations and collections
25
+
26
+ @user.expand_placeholders("[comments][text][/comments]")
27
+ => All comment texts
28
+
29
+ @user.expand_placeholders("[comments.1.text]")
30
+ => First comment text
31
+
32
+
33
+ == TODO
34
+
35
+ This gems still relies on king_views with king_format, for money, date
36
+ formatting. We will outsource king_format into its own gem and remove more
37
+ SalesKing internal dependencies.
38
+
39
+ == Installation
40
+
41
+ Add this line to your application's Gemfile:
42
+
43
+ gem 'king_placeholder'
44
+
45
+ And then execute:
46
+
47
+ $ bundle
48
+
49
+ Or install it yourself as:
50
+
51
+ $ gem install king_placeholder
52
+
53
+ == Usage
54
+
55
+ See specs
56
+
57
+ == Contributing
58
+
59
+ 1. Fork it
60
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
62
+ 4. Push to the branch (`git push origin my-new-feature`)
63
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec'
4
+ require 'rspec/core/rake_task'
5
+ require 'rdoc/task'
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new
9
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/king_placeholder/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Georg Leciejewski"]
6
+ gem.email = ["gl@salesking.eu"]
7
+ gem.description = %q{}
8
+ gem.summary = %q{Placeholder Parsing in Strings}
9
+ gem.homepage = 'https://github.com/salesking/king_placeholder.git'
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "king_placeholder"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = KingPlaceholder::VERSION
17
+
18
+ gem.add_runtime_dependency 'statemachine'
19
+ gem.add_runtime_dependency 'i18n'
20
+ gem.add_runtime_dependency 'activesupport'
21
+ gem.add_runtime_dependency 'actionpack'
22
+ #gem.add_runtime_dependency 'actionview'
23
+ #gem.add_runtime_dependency 'king_views'
24
+
25
+ gem.add_development_dependency 'activerecord'
26
+ gem.add_development_dependency 'rdoc'
27
+ gem.add_development_dependency 'rspec'
28
+ gem.add_development_dependency 'simplecov'
29
+ gem.add_development_dependency 'rake', '>= 0.9.2'
30
+ end
@@ -0,0 +1,207 @@
1
+ require 'statemachine'
2
+ require 'action_view' # king_views related suxs
3
+ require 'action_controller' # king_views
4
+ require 'king_views'
5
+ module KingPlaceholder
6
+ # Statemachine for placeholder substitution
7
+ # The statemachine is created and its state updated from within the Context
8
+ # object. This only holds the state definitions.
9
+ module ParseMachine
10
+
11
+ def self.create_with(machine_context)
12
+ return Statemachine.build do
13
+ # Origin State Event Destination State Action
14
+ # FROM EVENT TO ACTION
15
+ trans :waiting, :match, :matching, :prepare
16
+ state :matching do
17
+ on_entry :parse
18
+ end
19
+ trans :matching, :finished_matching, :finished, :cleanup
20
+ context machine_context
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ # Statemachine context for placeholder substitution
27
+ #
28
+ # === Example
29
+ # machine = ParserContext.new(obj, content)
30
+ # # send match event
31
+ # machine.sm.match
32
+ # machine.result
33
+ class ParseContext
34
+ include ::KingFormat::FormattingHelper
35
+ # reference to statemachine
36
+ attr_accessor :sm
37
+ # incoming string
38
+ attr_accessor :content
39
+ # Output string, with placeholders substituted
40
+ attr_accessor :result
41
+ # the current object
42
+ attr_accessor :obj
43
+ # hash parse options
44
+ attr_accessor :opts
45
+
46
+ # === Parameter
47
+ # obj<Object>:: any object responding to has_placeholder
48
+ # content<String>:: String containing placeholders
49
+ # opts<Hash{Symbol=>Mixed}>:: parse options
50
+ # :only<Array> => parse only the given placeholders
51
+ def initialize(obj, content, opts={})
52
+ @sm = ParseMachine.create_with(self) # init statemachine
53
+ @obj = obj
54
+ @content = content
55
+ @opts = opts
56
+ end
57
+
58
+ # Before matching is done this method set's up environment variables like
59
+ # current_language => i18n.locale
60
+ # TODO: decouple and outsource into before_method block to set env vars from outside
61
+ def prepare
62
+ init_locale #I18n.locale
63
+ set_format_opts # merge date/money format into thread var
64
+ # deep copy content, because otherwise result.gsub! breaks recursion
65
+ @result = @content.dup
66
+ end
67
+
68
+ # Match all placeholder(inside the brackets []) and replace them with their
69
+ # values
70
+ # See #expand_placeholders for docs
71
+ # When finished matching, triggers finished_matching event on statemachine
72
+ def parse
73
+ while match = @result.match(/\[((\w|\.)+)\]/)
74
+ @c_placeholder = match[0] # with brackets - current placeholder
75
+ @c_field = match[1] # without brackets - current field
76
+
77
+ check_current_prefix
78
+
79
+ if @c_field['.']
80
+ sub_object
81
+ elsif obj.respond_to?(@c_field) && ( @cur_collection = obj.send(@c_field) ).is_a?(Array)
82
+ sub_collection
83
+ else
84
+ sub_string
85
+ end
86
+ # If placeholder still exists here, it can't be recognized, sub with error
87
+ @result.gsub!(@c_placeholder, "UNKNOWN for #{obj.class.to_s}: #{@c_field}")
88
+ end
89
+ @sm.finished_matching
90
+ end
91
+
92
+ # When finished this method is called to cleanup f.ex. environment variables
93
+ # like I18n.locale
94
+ def cleanup
95
+ @current_language && @current_language.reset_locale
96
+ end
97
+
98
+ private
99
+
100
+ # Final destination of each placeholder in it's simple notation without
101
+ # namespace e.g. [price_to_pay]
102
+ def sub_string
103
+ return unless obj.is_placeholder?(@c_field)
104
+ # only parse some placeholders
105
+ # return if opts[:only] && !opts[:only].include?(@c_field)
106
+ value = strfval(obj, @c_field)
107
+ @result.gsub!(@c_placeholder, value.to_s) if value
108
+ end
109
+
110
+ # Namespaced notation, for related objects. We are in invoice which belongs
111
+ # to a company:
112
+ # [company.default_address.zip]
113
+ # instead of
114
+ # [invoice.company.default_address.zip]
115
+ def sub_object
116
+ splitted = @c_field.split('.')
117
+ object_name = splitted.first # just the first
118
+ field_names = splitted[1..-1] # all but the first
119
+ return unless object_name && obj.respond_to?(object_name) # the field exists
120
+ object = obj.send(object_name)
121
+ #check if its a collection => invoice.items and access is done by ary index items.0.name
122
+ if object.is_a?(Array) && ary_index = field_names.first[/\A\d*\z/] # [items.5.name] match digit only string
123
+ field_names.delete_at(0) # remove entry from field_names ary
124
+ # replace with empty string if the index does not exist or obj is empty
125
+ @result.gsub!(@c_placeholder, '') unless object = object[ary_index.to_i-1]
126
+ end
127
+
128
+ # Recurse and Let the referenced object do the expanding
129
+ if object.respond_to?(:expand_placeholders)
130
+ value = object.expand_placeholders("[#{field_names.join('.')}]")
131
+ @result.gsub!(@c_placeholder, value)
132
+ end
133
+ end
134
+
135
+ # The current field is the beginning of a group, which needs to be ended by
136
+ # [/field_name]
137
+ # e.g. [items] Item price: [price] \n [/items]
138
+ def sub_collection
139
+ if match = @result.match(/\[#{@c_field}\](.*)\[\/#{@c_field}\]/m) # the /m makes the dot match newlines, too!
140
+ whole_group = match[0]
141
+ inner_placeholders = match[1]
142
+ inner_result = ''
143
+ # Let the referenced object do the expanding by recursion if the collection knowns placeholders
144
+ @cur_collection.each do |item|
145
+ inner_result << item.expand_placeholders(inner_placeholders)
146
+ end if @cur_collection.first.respond_to?(:expand_placeholders)
147
+ @result.gsub!(whole_group, inner_result)
148
+ else
149
+ @result.gsub!(@c_placeholder, "END MISSING FOR #{@c_field}")
150
+ end
151
+ end
152
+
153
+ # Checks if the current field name contains the current object's class name.
154
+ # If the current class is an invoice, kick the prefix:
155
+ # invoice.number => number
156
+ def check_current_prefix
157
+ if @c_field['.']
158
+ splitted = @c_field.split('.')
159
+ object_name = splitted.first
160
+ if object_name && obj.class.name == object_name.classify
161
+ splitted.delete_at(0) # kick the object portion => invoice
162
+ @c_field = splitted.join('.') # glue the rest back together [number]
163
+ end
164
+ end
165
+ end
166
+
167
+ # set format options for KingFormat date/money helpers
168
+ # => strfval + strfmoney
169
+ # very important, without those all formatting in views screws up
170
+ def set_format_opts
171
+ Thread.current[:default_currency_format] = if defined?(::Company) && ::Company.current
172
+ ::Company.current.money_format
173
+ elsif obj.respond_to?(:company) && obj.company
174
+ obj.company.money_format
175
+ elsif defined?(::Company) && obj.is_a?(::Company)
176
+ obj.money_format
177
+ else
178
+ nil
179
+ end
180
+
181
+ Thread.current[:default_date_format] = if defined?(::Company) && ::Company.current
182
+ ::Company.current.date_format
183
+ elsif obj.respond_to?(:company) && obj.company
184
+ obj.company.date_format
185
+ elsif defined?(::Company) && obj.is_a?(::Company)
186
+ obj.date_format
187
+ else
188
+ nil
189
+ end
190
+ end
191
+
192
+ # Set i18n.locale to given or present custom company locale
193
+ # locale is memoized in instance @current_language.
194
+ # Only set if a company and the language is available
195
+ # === Parameter
196
+ # locale<String>:: locale code en, de,..
197
+ def init_locale(locale=nil)
198
+ if (locale || (obj.respond_to?(:language) && obj.language)) \
199
+ && obj.respond_to?(:company) && obj.company
200
+ @current_language ||= begin
201
+ # find lang in scope of company
202
+ ::Language.init_locale(obj.company, locale || obj.language)
203
+ end
204
+ end
205
+ end
206
+ end #class
207
+ end #module
@@ -0,0 +1,3 @@
1
+ module KingPlaceholder
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,116 @@
1
+ require 'king_placeholder/parse_context'
2
+ require 'active_support'
3
+ require 'active_support/version'
4
+
5
+ # Define fields/methods of the including class as placeholders.
6
+ # A Placeholder can be used inside any text string and will be replaced with a
7
+ # stringified, formatted value(by KingViews gem => KingFormat::FormattingHelper.strfval )
8
+ # Used for text snippets, PDF creation or E-Mail templates.
9
+
10
+ module KingPlaceholder
11
+
12
+ # sets :placeholders and init Class.placeholders as emtpy array on inclusion
13
+ def self.included(base)
14
+ if ActiveSupport::VERSION::MAJOR == 3 && ActiveSupport::VERSION::MINOR > 0
15
+ base.class_attribute :placeholders
16
+ else
17
+ base.send :class_inheritable_accessor, :placeholders
18
+ end
19
+ base.placeholders = []
20
+ base.extend(ClassMethods)
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ # Defines the fields returned by self.placeholders.
26
+ # Sets self.publish if empty.
27
+ # ==== Parameter
28
+ # fieldnames<Array[Symbol]>:: the names of the fields which are available
29
+ # throught the placeholder methods
30
+ def has_placeholders(*fieldnames)
31
+ self.placeholders = fieldnames
32
+ include InstanceMethods
33
+ end
34
+ end #ClassMethods
35
+
36
+ module InstanceMethods
37
+
38
+ # Check if a given field is declared as placeholder
39
+ # TODO check usage and/or move to class methods
40
+ # ==== Parameter
41
+ # fieldname<String|Symbol>:: the field to search in the placeholders array
42
+ # ==== Returns
43
+ # <Boolean>:: true if in
44
+ def is_placeholder?(fieldname)
45
+ self.class.placeholders.include?(fieldname.to_sym)
46
+ end
47
+
48
+ # Substitute placeholder in a string with their current values.
49
+ # It handles strings, arrays (of strings) or hashes (with string values)
50
+ # and returns data with the same data type e.g. if you put a hash, you will
51
+ # get a hash.
52
+ #
53
+ # ==== Examples
54
+ #
55
+ # Placeholders in text strings can be written in different notations.
56
+ #
57
+ # ===== Simple Notation:
58
+ #
59
+ # => [first_name]
60
+ # The fieldname is directly looked up on the current class:
61
+ # client.expand_placeholders("Hello [first_name]")
62
+ # => "Hello Herbert"
63
+ # invoice.expand_placeholders(["You owe me [price_to_pay]", "Invoice Nr. [number]"])
64
+ # => ["You owe me 495,00 EUR", "Invoice Nr. 123"]
65
+ #
66
+ # ===== Namespaced Notation
67
+ #
68
+ # => [company.organisation]
69
+ # If the prefix equals the type of the current object the field is looked up on it.
70
+ # client.expand_placeholders("Hello [client.first_name]")
71
+ # => "Hello Herbert"
72
+ #
73
+ # If the prefix is a single related object => Client :belongs_to Company,
74
+ # the substitution is delegated to that class.
75
+ # client.expand_placeholders("Belongs to [company.name]")
76
+ # => ""Belongs to Big Money Coorp."
77
+ # It goes down all the way:
78
+ # invoice.expand_placeholders("[client.company.default_address.zip]")
79
+ # => "50999"
80
+ #
81
+ # ===== Namespaced Notation with multiple related objects
82
+ #
83
+ # In a has_many relationship, related objects reside in an array, which can
84
+ # be reached using two different strategies.
85
+ #
86
+ # Access and Iterate over the whole Group:
87
+ # invoice.expand_placeholders("You bought: [items] [name] [/items]")
88
+ # => "You bought: Apple Banana Orange"
89
+ #
90
+ # Access a single object by its array index:
91
+ # invoice.expand_placeholders("You bought an [items.0.name] for [items.0.price]")
92
+ # => "You bought an Apple for 12 EUR"
93
+ #
94
+ # === Parameter
95
+ # content<Hash,Array, String>:: Collection, Hash or single string containing
96
+ # placeholders
97
+ # === Returns
98
+ # <Hash,Array, String>:: whatever type you threw in is also returned
99
+ def expand_placeholders(content, opts={})
100
+ if content.is_a?(Array) # Expand all array elements and go recursive
101
+ result = []
102
+ content.each{|element| result << self.expand_placeholders(element, opts) }
103
+ return result
104
+ elsif content.is_a?(Hash) # Expand all hash elements and go recursive
105
+ result = {}
106
+ content.each_pair{ |key,val| result[key] = self.expand_placeholders(val, opts) }
107
+ return result
108
+ else # Only proceed with strings
109
+ return content unless content.is_a?(String)
110
+ end
111
+ parser = KingPlaceholder::ParseContext.new(self, content, opts)
112
+ parser.sm.match
113
+ parser.result if parser.sm.state == :finished
114
+ end
115
+ end # instance methods
116
+ end # KingPlaceholders
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ class BaseModel
4
+ # include KingFormat::FormattingHelper
5
+ include KingPlaceholder
6
+ end
7
+
8
+ # Construct dummy models
9
+ class Master < BaseModel
10
+ attr_accessor :string_field
11
+ attr_accessor :details
12
+ attr_accessor :side
13
+ has_placeholders :string_field
14
+ end
15
+
16
+ class Side < BaseModel
17
+ attr_accessor :field
18
+ attr_accessor :master
19
+ has_placeholders :field
20
+ end
21
+
22
+ class Detail < BaseModel
23
+ include KingFormat::MoneyFields
24
+ attr_accessor :int_field, :money_field, :secret_field, :currency
25
+ attr_accessor :master
26
+ has_money_fields :money_field
27
+ has_placeholders :int_field, :money_field
28
+ end
29
+
30
+ describe 'Class with placeholders' do
31
+
32
+ before :each do
33
+ I18n.locale = :en_master
34
+ # Thread.current[:default_currency_format] = I18n.t(:'number.currency.format')
35
+ @record = Detail.new
36
+ @record.int_field = 1000
37
+ @record.money_field = 12.34
38
+ end
39
+
40
+ it 'should have native values' do
41
+ @record.int_field.should == 1000
42
+ @record.money_field.should == 12.34
43
+ end
44
+
45
+ it 'should have placeholder values' do
46
+ @record.expand_placeholders('[int_field]').should == '1000'
47
+ @record.expand_placeholders('[money_field]').should == '$12.34'
48
+ end
49
+
50
+ end
51
+
52
+ describe 'Expanding of strings containing placeholder' do
53
+
54
+ before :each do
55
+ I18n.locale = :en_master
56
+ @side = Side.new
57
+ @side.field = 123
58
+ @master = Master.new
59
+ @master.string_field = 'foo'
60
+ @master.side = @side
61
+ @side.master = @master
62
+
63
+ @detail1 = Detail.new
64
+ @detail1.int_field = 1001
65
+ @detail1.money_field = 12.34
66
+ @detail1.secret_field = 'top-secret'
67
+ @detail1.master = @master
68
+
69
+ @detail2 = Detail.new
70
+ @detail2.int_field = 1002
71
+ @detail2.money_field = 45.67
72
+ @detail2.secret_field = 'little secret'
73
+ @detail2.master = @master
74
+ @master.details = [@detail1, @detail2]
75
+ end
76
+
77
+ it 'should expand placeholders with no placeholders there' do
78
+ @detail1.expand_placeholders('without placeholder').should == 'without placeholder'
79
+ @detail1.expand_placeholders('[]').should == '[]'
80
+ @detail1.expand_placeholders('').should == ''
81
+ @detail1.expand_placeholders(nil).should == nil
82
+ @detail1.expand_placeholders("\n").should == "\n"
83
+ end
84
+
85
+ it 'should expand placeholders with simple fieldname' do
86
+ @detail1.expand_placeholders('[int_field]').should == '1001'
87
+ @detail1.expand_placeholders("[int_field]\n").should == "1001\n"
88
+ @detail1.expand_placeholders('[int_field]---[int_field]').should == '1001---1001'
89
+ @detail1.expand_placeholders('[int_field]---[money_field]').should == '1001---$12.34'
90
+ end
91
+
92
+ it 'should not expand placeholder for secret field' do
93
+ @detail1.expand_placeholders('[secret_field]').should == 'UNKNOWN for Detail: secret_field'
94
+ end
95
+
96
+ it 'should expand placeholder with namespaced fieldname' do
97
+ @detail1.expand_placeholders('[master.string_field]').should == 'foo'
98
+ @detail1.expand_placeholders('[master.string_field] [int_field]').should == 'foo 1001'
99
+ end
100
+
101
+ it 'should expand placeholder with namespaced to self fieldname' do
102
+ @detail1.expand_placeholders('[detail.int_field]').should == '1001'
103
+ @detail1.expand_placeholders('[detail.master.string_field] [detail.int_field]').should == 'foo 1001'
104
+ end
105
+
106
+ it 'should expand with multiple steps' do
107
+ @detail1.expand_placeholders('[master.side.field]').should == '123'
108
+ end
109
+
110
+ it 'should expand placeholder with not existing namespaces' do
111
+ @detail1.expand_placeholders('[nothing.string_field]').should == 'UNKNOWN for Detail: nothing.string_field'
112
+ end
113
+
114
+ it 'should expand placeholder with wrong namespaces' do
115
+ @detail1.expand_placeholders('[master.this.are.too.much.namespaces]').should == 'UNKNOWN for Master: this.are.too.much.namespaces'
116
+ @detail1.expand_placeholders('[this.are.too.much.namespaces]').should == 'UNKNOWN for Detail: this.are.too.much.namespaces'
117
+ @detail1.expand_placeholders('[...]').should == 'UNKNOWN for Detail: ...'
118
+ @detail1.expand_placeholders('[unknown]').should == 'UNKNOWN for Detail: unknown'
119
+ end
120
+
121
+ it 'should expand placeholder group' do
122
+ @master.expand_placeholders('[details][int_field]\n[/details]').should == '1001\n1002\n'
123
+ @master.expand_placeholders('[details]Test:[int_field][/details]').should == 'Test:1001Test:1002'
124
+ @master.expand_placeholders("[details][int_field]\n[/details]").should == "1001\n1002\n"
125
+
126
+ @master.expand_placeholders('[details][foo][/details]').should == 'UNKNOWN for Detail: fooUNKNOWN for Detail: foo'
127
+ end
128
+
129
+ it 'should expand valid but empty placeholder group with empty string' do
130
+ master = Master.new
131
+ master.details = []
132
+ master.expand_placeholders('[details][int_field][/details]').should == ''
133
+ end
134
+
135
+ it 'should expand single item from a placeholder group' do
136
+ @master.details.inspect
137
+ @master.expand_placeholders('[details.1.int_field]').should == '1001'
138
+ @master.expand_placeholders('[details.2.int_field]').should == '1002'
139
+ end
140
+
141
+ it 'should expand single item in empty placeholder group with empty string' do
142
+ master = Master.new
143
+ master.details = []
144
+ master.expand_placeholders('[details.0.int_field]').should == ''
145
+ end
146
+
147
+ it 'should expand empty single item from placeholder group with empty string' do
148
+ @master.expand_placeholders('[details.10.int_field]').should == ''
149
+ end
150
+
151
+ it 'should expand placeholder group for non ending group' do
152
+ @master.expand_placeholders('[details][int_field]').should == 'END MISSING FOR detailsUNKNOWN for Master: int_field'
153
+ end
154
+
155
+ # TODO: Make this possible!
156
+ # it "should expand placeholder group if referenced with namespace" do
157
+ # @master.expand_placeholders("[master.details][int_field][end]").should == "10011002"
158
+ # end
159
+
160
+ it 'should expand placeholders in an array' do
161
+ @detail1.expand_placeholders(['[int_field]', '[money_field]', 'static']).should == ['1001', '$12.34', 'static']
162
+ end
163
+
164
+ it 'should expand placeholders in a hash' do
165
+ @detail1.expand_placeholders( :key1 => '[int_field]',
166
+ :key2 => '[money_field]',
167
+ :key3 => 'static'
168
+ ).should ==
169
+ { :key1 => '1001',
170
+ :key2 => '$12.34',
171
+ :key3 => 'static' }
172
+ end
173
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ $:.unshift(File.dirname(__FILE__))
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ ENV["RAILS_ENV"] ||= 'test'
5
+
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter "/json/"
9
+ end
10
+ SimpleCov.coverage_dir 'coverage'
11
+
12
+ require 'rubygems'
13
+ require 'rspec'
14
+ require 'active_record'
15
+ require 'king_placeholder'
16
+
17
+
18
+
19
+ RSpec.configure do |config|
20
+ end
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: king_placeholder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Georg Leciejewski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: statemachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: i18n
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activesupport
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: actionpack
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: activerecord
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rdoc
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: simplecov
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rake
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: 0.9.2
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: 0.9.2
158
+ description: ''
159
+ email:
160
+ - gl@salesking.eu
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - .gitignore
166
+ - Gemfile
167
+ - LICENSE
168
+ - README.rdoc
169
+ - Rakefile
170
+ - king_placeholder.gemspec
171
+ - lib/king_placeholder.rb
172
+ - lib/king_placeholder/parse_context.rb
173
+ - lib/king_placeholder/version.rb
174
+ - spec/king_placeholder_spec.rb
175
+ - spec/spec_helper.rb
176
+ homepage: https://github.com/salesking/king_placeholder.git
177
+ licenses: []
178
+ post_install_message:
179
+ rdoc_options: []
180
+ require_paths:
181
+ - lib
182
+ required_ruby_version: !ruby/object:Gem::Requirement
183
+ none: false
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ segments:
189
+ - 0
190
+ hash: 2667406904980012047
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
+ none: false
193
+ requirements:
194
+ - - ! '>='
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ segments:
198
+ - 0
199
+ hash: 2667406904980012047
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 1.8.24
203
+ signing_key:
204
+ specification_version: 3
205
+ summary: Placeholder Parsing in Strings
206
+ test_files:
207
+ - spec/king_placeholder_spec.rb
208
+ - spec/spec_helper.rb