stockor-core 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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +6 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +674 -0
- data/README.md +88 -0
- data/Rakefile +58 -0
- data/config/database.yml +9 -0
- data/db/migrate/20120110142845_create_skr_sequential_ids.rb +35 -0
- data/db/migrate/20140202185309_create_skr_gl_accounts.rb +15 -0
- data/db/migrate/20140202193316_create_skr_gl_periods.rb +16 -0
- data/db/migrate/20140202193318_create_skr_gl_transactions.rb +14 -0
- data/db/migrate/20140202193319_create_skr_gl_postings.rb +16 -0
- data/db/migrate/20140202193700_create_skr_gl_manual_entries.rb +13 -0
- data/db/migrate/20140213040608_create_skr_payment_terms.rb +16 -0
- data/db/migrate/20140220031700_create_skr_addresses.rb +19 -0
- data/db/migrate/20140220031800_create_skr_locations.rb +19 -0
- data/db/migrate/20140220190836_create_skr_vendors.rb +22 -0
- data/db/migrate/20140220203029_create_skr_customers.rb +22 -0
- data/db/migrate/20140224034759_create_skr_skus.rb +22 -0
- data/db/migrate/20140225032853_create_skr_sku_locs.rb +21 -0
- data/db/migrate/20140320030501_create_skr_uoms.rb +19 -0
- data/db/migrate/20140321031604_create_skr_sku_vendors.rb +18 -0
- data/db/migrate/20140322012143_create_skr_ia_reasons.rb +14 -0
- data/db/migrate/20140322014401_create_skr_inventory_adjustments.rb +16 -0
- data/db/migrate/20140322023453_create_skr_ia_lines.rb +18 -0
- data/db/migrate/20140322035024_create_skr_sku_trans.rb +21 -0
- data/db/migrate/20140322223912_create_skr_sales_orders.rb +27 -0
- data/db/migrate/20140322223920_create_skr_so_lines.rb +25 -0
- data/db/migrate/20140323001446_create_so_details_view.rb +81 -0
- data/db/migrate/20140327202102_create_skr_purchase_orders.rb +20 -0
- data/db/migrate/20140327202107_create_skr_po_lines.rb +25 -0
- data/db/migrate/20140327202207_create_skr_pick_tickets.rb +16 -0
- data/db/migrate/20140327202209_create_skr_pt_lines.rb +23 -0
- data/db/migrate/20140327224000_create_skr_invoices.rb +25 -0
- data/db/migrate/20140327224002_create_skr_inv_lines.rb +23 -0
- data/db/migrate/20140330232808_create_skr_sku_loc_details_view.rb +31 -0
- data/db/migrate/20140330232810_create_skr_sku_qty_details_view.rb +48 -0
- data/db/migrate/20140400164729_create_skr_vouchers.rb +22 -0
- data/db/migrate/20140400164733_create_skr_vo_lines.rb +21 -0
- data/db/migrate/20140401164729_create_skr_po_receipt.rb +16 -0
- data/db/migrate/20140401164740_create_skr_por_line.rb +21 -0
- data/db/migrate/20140422024010_create_skr_inv_details_view.rb +42 -0
- data/lib/generators/stockor/migrations/install_generator.rb +42 -0
- data/lib/skr/address.rb +97 -0
- data/lib/skr/business_entity.rb +25 -0
- data/lib/skr/concerns/acts_as_uom.rb +47 -0
- data/lib/skr/concerns/all.rb +30 -0
- data/lib/skr/concerns/association_extensions.rb +85 -0
- data/lib/skr/concerns/attr_accessor_with_default.rb +54 -0
- data/lib/skr/concerns/code_identifier.rb +43 -0
- data/lib/skr/concerns/export_associations.rb +52 -0
- data/lib/skr/concerns/export_join_tables.rb +39 -0
- data/lib/skr/concerns/export_methods.rb +104 -0
- data/lib/skr/concerns/export_scope.rb +66 -0
- data/lib/skr/concerns/exported_limit_evaluator.rb +17 -0
- data/lib/skr/concerns/gl_tran_extensions.rb +18 -0
- data/lib/skr/concerns/has_gl_transaction.rb +67 -0
- data/lib/skr/concerns/has_sku_loc_lines.rb +47 -0
- data/lib/skr/concerns/immutable_model.rb +32 -0
- data/lib/skr/concerns/inv_extensions.rb +24 -0
- data/lib/skr/concerns/is_order_like.rb +47 -0
- data/lib/skr/concerns/is_sku_loc_line.rb +65 -0
- data/lib/skr/concerns/json_attribute_access.rb +55 -0
- data/lib/skr/concerns/locked_fields.rb +84 -0
- data/lib/skr/concerns/pt_extensions.rb +22 -0
- data/lib/skr/concerns/pub_sub.rb +105 -0
- data/lib/skr/concerns/queries.rb +20 -0
- data/lib/skr/concerns/random_hash_code.rb +40 -0
- data/lib/skr/concerns/sanitize_json.rb +49 -0
- data/lib/skr/concerns/sku_extensions.rb +52 -0
- data/lib/skr/concerns/so_extensions.rb +30 -0
- data/lib/skr/concerns/state_machine.rb +62 -0
- data/lib/skr/concerns/track_modifications.rb +48 -0
- data/lib/skr/concerns/visible_id_identifier.rb +53 -0
- data/lib/skr/core.rb +30 -0
- data/lib/skr/core/configuration.rb +87 -0
- data/lib/skr/core/db.rb +82 -0
- data/lib/skr/core/db/migration_helpers.rb +178 -0
- data/lib/skr/core/db/migrations.rb +15 -0
- data/lib/skr/core/db/seed.rb +46 -0
- data/lib/skr/core/db/seed/chart_of_accounts.yml +168 -0
- data/lib/skr/core/db/seed/payment_terms.yml +60 -0
- data/lib/skr/core/logger.rb +36 -0
- data/lib/skr/core/numbers.rb +71 -0
- data/lib/skr/core/rails_engine.rb +5 -0
- data/lib/skr/core/standard_pricing_provider.rb +15 -0
- data/lib/skr/core/strings.rb +57 -0
- data/lib/skr/core/testing.rb +11 -0
- data/lib/skr/core/testing/assertions.rb +44 -0
- data/lib/skr/core/testing/fixtures.rb +25 -0
- data/lib/skr/core/testing/fixtures/skr/addresses.yml +53 -0
- data/lib/skr/core/testing/fixtures/skr/customers.yml +43 -0
- data/lib/skr/core/testing/fixtures/skr/gl_accounts.yml +86 -0
- data/lib/skr/core/testing/fixtures/skr/gl_manual_entries.yml +4 -0
- data/lib/skr/core/testing/fixtures/skr/gl_periods.yml +26 -0
- data/lib/skr/core/testing/fixtures/skr/gl_postings.yml +88 -0
- data/lib/skr/core/testing/fixtures/skr/gl_transactions.yml +35 -0
- data/lib/skr/core/testing/fixtures/skr/ia_lines.yml +7 -0
- data/lib/skr/core/testing/fixtures/skr/ia_reasons.yml +15 -0
- data/lib/skr/core/testing/fixtures/skr/inv_lines.yml +22 -0
- data/lib/skr/core/testing/fixtures/skr/inventory_adjustments.yml +6 -0
- data/lib/skr/core/testing/fixtures/skr/invoices.yml +10 -0
- data/lib/skr/core/testing/fixtures/skr/locations.yml +24 -0
- data/lib/skr/core/testing/fixtures/skr/payment_terms.yml +42 -0
- data/lib/skr/core/testing/fixtures/skr/pick_tickets.yml +4 -0
- data/lib/skr/core/testing/fixtures/skr/po_lines.yml +44 -0
- data/lib/skr/core/testing/fixtures/skr/po_receipts.yml +5 -0
- data/lib/skr/core/testing/fixtures/skr/por_lines.yml +13 -0
- data/lib/skr/core/testing/fixtures/skr/pt_lines.yml +12 -0
- data/lib/skr/core/testing/fixtures/skr/purchase_orders.yml +16 -0
- data/lib/skr/core/testing/fixtures/skr/sales_orders.yml +32 -0
- data/lib/skr/core/testing/fixtures/skr/sku_locs.yml +78 -0
- data/lib/skr/core/testing/fixtures/skr/sku_trans.yml +9 -0
- data/lib/skr/core/testing/fixtures/skr/sku_vendors.yml +35 -0
- data/lib/skr/core/testing/fixtures/skr/skus.yml +50 -0
- data/lib/skr/core/testing/fixtures/skr/so_lines.yml +71 -0
- data/lib/skr/core/testing/fixtures/skr/uoms.yml +61 -0
- data/lib/skr/core/testing/fixtures/skr/vendors.yml +48 -0
- data/lib/skr/core/testing/fixtures/skr/vo_lines.yml +12 -0
- data/lib/skr/core/testing/fixtures/skr/vouchers.yml +8 -0
- data/lib/skr/core/testing/helper.rb +18 -0
- data/lib/skr/core/testing/test_case.rb +17 -0
- data/lib/skr/core/version.rb +5 -0
- data/lib/skr/customer.rb +34 -0
- data/lib/skr/gl_account.rb +56 -0
- data/lib/skr/gl_manual_entry.rb +31 -0
- data/lib/skr/gl_period.rb +13 -0
- data/lib/skr/gl_posting.rb +54 -0
- data/lib/skr/gl_transaction.rb +174 -0
- data/lib/skr/ia_line.rb +129 -0
- data/lib/skr/ia_reason.rb +16 -0
- data/lib/skr/inv_line.rb +90 -0
- data/lib/skr/inventory_adjustment.rb +60 -0
- data/lib/skr/invoice.rb +159 -0
- data/lib/skr/location.rb +31 -0
- data/lib/skr/model.rb +37 -0
- data/lib/skr/null_user.rb +30 -0
- data/lib/skr/payment_term.rb +30 -0
- data/lib/skr/pick_ticket.rb +71 -0
- data/lib/skr/po_line.rb +69 -0
- data/lib/skr/po_receipt.rb +51 -0
- data/lib/skr/por_line.rb +80 -0
- data/lib/skr/pt_line.rb +74 -0
- data/lib/skr/purchase_order.rb +112 -0
- data/lib/skr/sales_order.rb +159 -0
- data/lib/skr/sequential_id.rb +23 -0
- data/lib/skr/sku.rb +99 -0
- data/lib/skr/sku_loc.rb +94 -0
- data/lib/skr/sku_tran.rb +111 -0
- data/lib/skr/sku_vendor.rb +26 -0
- data/lib/skr/so_line.rb +159 -0
- data/lib/skr/uom.rb +63 -0
- data/lib/skr/user_proxy.rb +60 -0
- data/lib/skr/validators/all.rb +2 -0
- data/lib/skr/validators/email.rb +17 -0
- data/lib/skr/validators/set.rb +18 -0
- data/lib/skr/vendor.rb +33 -0
- data/lib/skr/vo_line.rb +35 -0
- data/lib/skr/voucher.rb +119 -0
- data/lib/stockor/core.rb +9 -0
- data/stockor-core.gemspec +37 -0
- data/tasks/migrations.rake +23 -0
- data/tasks/publish.rake +8 -0
- data/test/address_test.rb +57 -0
- data/test/concerns/attr_with_default_test.rb +45 -0
- data/test/concerns/code_identifier_test.rb +46 -0
- data/test/concerns/export_associations_test.rb +7 -0
- data/test/concerns/export_methods_test.rb +31 -0
- data/test/concerns/export_scope_test.rb +16 -0
- data/test/concerns/exported_limits_test.rb +47 -0
- data/test/concerns/json_attribute_access_test.rb +27 -0
- data/test/concerns/pub_sub_test.rb +83 -0
- data/test/concerns/sanitize_json_test.rb +47 -0
- data/test/core/configuration_test.rb +24 -0
- data/test/core/numbers_test.rb +26 -0
- data/test/core/strings_test.rb +41 -0
- data/test/customer_test.rb +34 -0
- data/test/gl_account_test.rb +23 -0
- data/test/gl_manual_entry_test.rb +17 -0
- data/test/gl_period_test.rb +12 -0
- data/test/gl_posting_test.rb +39 -0
- data/test/gl_transaction_test.rb +58 -0
- data/test/ia_line_test.rb +68 -0
- data/test/ia_reason_test.rb +11 -0
- data/test/inv_line_test.rb +58 -0
- data/test/inventory_adjustment_test.rb +72 -0
- data/test/invoice_test.rb +63 -0
- data/test/location_test.rb +10 -0
- data/test/payment_term_test.rb +25 -0
- data/test/pick_ticket_test.rb +27 -0
- data/test/po_line_test.rb +36 -0
- data/test/po_receipt_test.rb +93 -0
- data/test/por_line_test.rb +79 -0
- data/test/pt_line_test.rb +29 -0
- data/test/purchase_order_test.rb +44 -0
- data/test/sales_order_test.rb +74 -0
- data/test/sku_loc_test.rb +50 -0
- data/test/sku_test.rb +45 -0
- data/test/sku_tran_test.rb +21 -0
- data/test/sku_vendor_test.rb +13 -0
- data/test/so_line_test.rb +89 -0
- data/test/test_helper.rb +1 -0
- data/test/uom_test.rb +14 -0
- data/test/vendor_test.rb +35 -0
- data/test/vo_line_test.rb +20 -0
- data/test/voucher_test.rb +35 -0
- data/yard_ext/all.rb +9 -0
- data/yard_ext/code_identifier_handler.rb +33 -0
- data/yard_ext/concern_meta_methods.rb +60 -0
- data/yard_ext/config_options.rb +27 -0
- data/yard_ext/exported_scope.rb +4 -0
- data/yard_ext/immutable_handler.rb +17 -0
- data/yard_ext/json_attr_accessor.rb +22 -0
- data/yard_ext/locked_fields_handler.rb +21 -0
- data/yard_ext/templates/default/layout/html/layout.erb +20 -0
- data/yard_ext/templates/default/method_details/html/github_link.erb +1 -0
- data/yard_ext/templates/default/method_details/setup.rb +3 -0
- data/yard_ext/validators.rb +1 -0
- data/yard_ext/visible_id_handler.rb +38 -0
- metadata +448 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Skr::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module AttrAccessorWithDefault
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
|
|
9
|
+
# defines a attr_accessor with a default value
|
|
10
|
+
# @param name [ Symbol ] name of the attribute
|
|
11
|
+
# @param default [ Object, lambda, Proc ] if a value is given, be aware that it will be shared between instances
|
|
12
|
+
# @example
|
|
13
|
+
#
|
|
14
|
+
# Shared = Struct.new(:str)
|
|
15
|
+
# class AttrTestClass
|
|
16
|
+
# include AttrAccessorWithDefault
|
|
17
|
+
# attr_accessor_with_default :non_copying, ->{ Shared.new("a default string") }
|
|
18
|
+
# attr_accessor_with_default :shared, Shared.new("a default string")
|
|
19
|
+
# end
|
|
20
|
+
# a = AttrTestClass.new
|
|
21
|
+
# b = AttrTestClass.new
|
|
22
|
+
# a.non_copying.str #=> "a default string"
|
|
23
|
+
# a.non_copying.str = "new_string" #=> "new string"
|
|
24
|
+
# b.non_copying.str #=> "a default string"
|
|
25
|
+
#
|
|
26
|
+
# a.shared.str #=> "a default string"
|
|
27
|
+
# b.shared.str #=> "a default string"
|
|
28
|
+
# a.shared.str = "new string" #=> "new string"
|
|
29
|
+
# b.shared.str #=> "new string"
|
|
30
|
+
|
|
31
|
+
def attr_accessor_with_default( name, default )
|
|
32
|
+
attr_writer name
|
|
33
|
+
attr_reader_with_default( name, default )
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def attr_reader_with_default( name, default )
|
|
37
|
+
module_eval do
|
|
38
|
+
define_method( name ) do
|
|
39
|
+
class << self; self; end.class_eval do
|
|
40
|
+
attr_reader( name )
|
|
41
|
+
end
|
|
42
|
+
if instance_variables.include? "@#{name}"
|
|
43
|
+
instance_variable_get( "@#{name}" )
|
|
44
|
+
else
|
|
45
|
+
instance_variable_set( "@#{name}", default.is_a?(Proc) ? default.call : default )
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Skr
|
|
2
|
+
module Concerns
|
|
3
|
+
|
|
4
|
+
# @see ClassMethods
|
|
5
|
+
module CodeIdentifier
|
|
6
|
+
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
# ### Code Identifier Concern
|
|
10
|
+
# This adds the {#has_code_identifier} class method
|
|
11
|
+
module ClassMethods
|
|
12
|
+
|
|
13
|
+
# A 2-10 character string that identifies an entity, such as a Customer, Vendor, or SKU.
|
|
14
|
+
# The code is often assigned by the user, but may also
|
|
15
|
+
# be auto-generated by {Skr::Core::Strings.code_identifier}
|
|
16
|
+
#
|
|
17
|
+
# @param max_length [Integer] how long the code is allowed to be
|
|
18
|
+
# @param from [Symbol], method to call for a string to base the code upon
|
|
19
|
+
def has_code_identifier( from: nil, max_length: 10 )
|
|
20
|
+
|
|
21
|
+
validates :code, :length=>2..max_length, :presence=>true, :uniqueness=>true
|
|
22
|
+
|
|
23
|
+
alias_attribute :record_identifier, :code
|
|
24
|
+
|
|
25
|
+
if from
|
|
26
|
+
before_validation(:on=>:create) do
|
|
27
|
+
source = self[from]
|
|
28
|
+
unless source.blank?
|
|
29
|
+
self.code ||= Skr::Core::Strings.code_identifier( source, length:max_length )
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
before_validation do
|
|
35
|
+
self.code = self.code.upcase unless self.code.blank?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Skr::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module ExportAssociations
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :exported_associations
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
# Mark associations as exported, meaning they can be quered against and optionally written to
|
|
13
|
+
# @param associations [list of Symbols]
|
|
14
|
+
# @option options [Boolean] :writable should the associations accept nested attributes
|
|
15
|
+
# @option options [Boolean] :mandatory should the association be included with it's parent at all times?
|
|
16
|
+
# @option options [Symbol referring to a method, lambda] :limit restrict to Users for whom true is returned
|
|
17
|
+
def export_associations( *associations )
|
|
18
|
+
self.exported_associations ||= {}
|
|
19
|
+
associations.flatten!
|
|
20
|
+
options = associations.extract_options!
|
|
21
|
+
associations.each do | assoc_name |
|
|
22
|
+
self.exported_associations[ assoc_name.to_sym ] = options
|
|
23
|
+
if options[:writable]
|
|
24
|
+
accepts_nested_attributes_for( assoc_name, options.except(:writable, :optional) )
|
|
25
|
+
end
|
|
26
|
+
if false == options[:optional]
|
|
27
|
+
self.export_methods( *assoc_name, { :optional=>false })
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# If the association is exported it may be queried against and it's data included
|
|
33
|
+
# along with the parent record. It may not be written to unless the :writable flag
|
|
34
|
+
# was set, in which case it'll be allowed by has_exported_association?
|
|
35
|
+
def has_exported_association?( association, user )
|
|
36
|
+
self.exported_associations &&
|
|
37
|
+
( options = self.exported_associations[ association.to_sym ] ) &&
|
|
38
|
+
evaluate_export_limit( user, :association, association, options[:limit] )
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Does the association allow writes? True if the association has been exported
|
|
42
|
+
# and it accepts nested attributes
|
|
43
|
+
def has_exported_nested_attribute?( association, user )
|
|
44
|
+
self.nested_attributes_options[ association.to_sym ] &&
|
|
45
|
+
self.has_exported_association?( association, user )
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Skr::Concerns
|
|
2
|
+
|
|
3
|
+
module ExportJoinTables
|
|
4
|
+
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
class_attribute :exported_join_tables
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
# Mark a joined table as safe to be included in a query
|
|
13
|
+
# Primarily used for joining a model to a view for access to summarized data
|
|
14
|
+
def export_join_tables( *tables )
|
|
15
|
+
include ExportedLimitEvaluator
|
|
16
|
+
self.exported_join_tables ||= []
|
|
17
|
+
tables.flatten!
|
|
18
|
+
options = tables.extract_options!
|
|
19
|
+
tables.each do | join_name |
|
|
20
|
+
self.exported_join_tables << {
|
|
21
|
+
name: join_name,
|
|
22
|
+
limit: options[:limit]
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Has the join been marked as safe?
|
|
29
|
+
def has_exported_join_table?(name, user)
|
|
30
|
+
return true if name == 'details' # "details" is reserved for views and is always allowed
|
|
31
|
+
self.exported_join_tables && self.exported_join_tables.detect{ | join |
|
|
32
|
+
join[:name] == name && evaluate_export_limit( user, :join, join[:name], join[:limit] )
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module Skr::Concerns
|
|
2
|
+
|
|
3
|
+
# @see ClassMethods
|
|
4
|
+
module ExportMethods
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
|
|
9
|
+
# methods that the model has exported. Not intended for querying directly.
|
|
10
|
+
# The API and interested parties should call {#has_exported_method?}
|
|
11
|
+
class_attribute :exported_methods
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
|
|
16
|
+
# Called by a model to export methods to the API
|
|
17
|
+
# An exported method will be called and it's results returned along with the models
|
|
18
|
+
# JSON representation.
|
|
19
|
+
# @option options [Boolean] :optional if false, the method will always be called and included
|
|
20
|
+
# @option options [Symbol, Array of Symbols] :depends name(s) of associations that should
|
|
21
|
+
# be pre-loaded before calling method. Intended to prevent N+1 queries
|
|
22
|
+
def export_methods( *method_names )
|
|
23
|
+
method_names.flatten!
|
|
24
|
+
options = method_names.extract_options!
|
|
25
|
+
self.exported_methods ||= {}
|
|
26
|
+
method_names.map do | name |
|
|
27
|
+
exported_methods[ name.to_sym] = options
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Convenience method to create a Rails delegation and export the resulting method
|
|
32
|
+
# @example
|
|
33
|
+
# class Foo < Skr::Model
|
|
34
|
+
# belongs_to :bar
|
|
35
|
+
# delegate_and_export :bar_name, :optional=>false
|
|
36
|
+
# end
|
|
37
|
+
#
|
|
38
|
+
# foo = Foo.new
|
|
39
|
+
# foo.bar_name #=> calls foo.bar.name
|
|
40
|
+
# Foo.has_exported_method?( :bar_name ) #=> true
|
|
41
|
+
# skr_to_json( foo ) #=> will first load the bar association, then
|
|
42
|
+
# call foo.bar_name and include it's result in the JSON
|
|
43
|
+
def delegate_and_export( *names )
|
|
44
|
+
options = names.extract_options!
|
|
45
|
+
names.each do | name |
|
|
46
|
+
target,field = name.to_s.split(/_(?=[^_]+(?: |$))| /)
|
|
47
|
+
delegate_and_export_field( target, field, optional: options[:optional], limit: options[:limit] )
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# For situations where the delegate_and_export method guesses wrong on the association and field names
|
|
52
|
+
# @param target [Association] association to delegate to
|
|
53
|
+
# @param field [Symbol] method on Association to call
|
|
54
|
+
# @param optional [Boolean] should the method be called and results included all the time,
|
|
55
|
+
# or only if specifically requested
|
|
56
|
+
# @param limit [Symbol referring to a method, lambda] restrict to Users for whom true is returned
|
|
57
|
+
# @example
|
|
58
|
+
# class PoLine < Skr::Model
|
|
59
|
+
# belongs_to :purchase_order
|
|
60
|
+
# delegate_and_export :purchase_order_visible_id
|
|
61
|
+
# end
|
|
62
|
+
# would generate a method ``purchase_order_visible_id`` that would call ``purchase_order_visible.id``
|
|
63
|
+
# This would do the right thing:
|
|
64
|
+
# class PoLine < Skr::Model
|
|
65
|
+
# belongs_to :purchase_order
|
|
66
|
+
# delegate_and_export_field :purchase_order, :visible_id
|
|
67
|
+
# end
|
|
68
|
+
def delegate_and_export_field( target, field, optional: true, limit: nil )
|
|
69
|
+
delegate field, to: target, prefix: target, allow_nil: true
|
|
70
|
+
self.export_methods( "#{target}_#{field}", { depends: target.to_sym,
|
|
71
|
+
optional: optional,
|
|
72
|
+
limit: limit } )
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Check if the method can be called by user
|
|
76
|
+
def has_exported_method?( name, user )
|
|
77
|
+
if self.exported_methods && ( method_options = self.exported_methods[ name.to_sym ] )
|
|
78
|
+
return evaluate_export_limit( user, :method, name, method_options[:limit] )
|
|
79
|
+
else
|
|
80
|
+
return false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Retrieve the list of dependent associations for the given methods
|
|
85
|
+
# Also includes methods that were exported as ``optional: false``
|
|
86
|
+
# @param requested_methods [Array] methods that the user has requested be executed and added to the JSON payload
|
|
87
|
+
# @return [Array] list of associations that should be included in query
|
|
88
|
+
def exported_method_dependancies( requested_methods )
|
|
89
|
+
requested_methods.map!(&:to_sym)
|
|
90
|
+
return [] if self.exported_methods.blank?
|
|
91
|
+
depends = self.exported_methods.each_with_object(Array.new) do | kv, result |
|
|
92
|
+
( export, options ) = kv
|
|
93
|
+
if options[:depends] && ( false == options[:optional] || requested_methods.include?(export) )
|
|
94
|
+
result << options[:depends]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
depends.uniq
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require_relative 'exported_limit_evaluator'
|
|
2
|
+
|
|
3
|
+
module Skr::Concerns
|
|
4
|
+
|
|
5
|
+
# @see ClassMethods
|
|
6
|
+
module ExportScope
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
# scopes that the model has exported. Not intended for querying directly.
|
|
11
|
+
# The API and interested parties should call {#has_exported_scope?}
|
|
12
|
+
class_attribute :exported_scopes
|
|
13
|
+
extend ExportedLimitEvaluator
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# ### Mark a scope as "exportable"
|
|
17
|
+
#
|
|
18
|
+
# An exported scope is safe for querying by external clients over the API.
|
|
19
|
+
# The scope should always:
|
|
20
|
+
#
|
|
21
|
+
# * Safely escape data *(should __ALWAYS__ do this anyway, but it bears mentioning again)*
|
|
22
|
+
# * Be relatively simple and complete quickly.
|
|
23
|
+
# * Provide value to the client that it cannot obtain by using normal query methods
|
|
24
|
+
module ClassMethods
|
|
25
|
+
def scope(name, body, options = {}, &block)
|
|
26
|
+
super(name, body, &block)
|
|
27
|
+
if (export = options[:export])
|
|
28
|
+
export_scope(name, body, limit: (export == true ? nil : export[:limit]))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Mark scope as query-able by the API.
|
|
33
|
+
# @param name [Symbol,String] Rails will create a class method with this name
|
|
34
|
+
# @param query [lambda] Arel query. This is passed off to Rail's for setting up the scope.
|
|
35
|
+
# @param limit [Symbol referring to a Class method name, lambda]
|
|
36
|
+
# If given, this will be queried by the API to determining if a given user may call the scope
|
|
37
|
+
# @return nil
|
|
38
|
+
def export_scope(name, query, limit: nil)
|
|
39
|
+
include ExportedLimitEvaluator
|
|
40
|
+
|
|
41
|
+
self.exported_scopes ||= Hash.new
|
|
42
|
+
self.exported_scopes[name.to_sym] = {
|
|
43
|
+
scope: scope(name, query),
|
|
44
|
+
name: name,
|
|
45
|
+
limit: limit
|
|
46
|
+
}
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The api can query this to determine if the scope is safe to be called
|
|
51
|
+
# from the API by [user]
|
|
52
|
+
# @param name [Symbol,String] name of scope
|
|
53
|
+
# @param user [User] who is performing the request.
|
|
54
|
+
# This is passed off to the method or lambda that was given as the limit argument in {#export_scope}
|
|
55
|
+
|
|
56
|
+
def has_exported_scope?(name, user)
|
|
57
|
+
if self.exported_scopes && ( scope_options = self.exported_scopes[ name.to_sym ] )
|
|
58
|
+
return evaluate_export_limit( user, :scope, name, scope_options[:limit] )
|
|
59
|
+
else
|
|
60
|
+
return false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Skr::Concerns
|
|
2
|
+
|
|
3
|
+
module ExportedLimitEvaluator
|
|
4
|
+
|
|
5
|
+
def evaluate_export_limit( user, type, name, limit )
|
|
6
|
+
if limit.nil?
|
|
7
|
+
true
|
|
8
|
+
elsif limit.is_a?(Symbol)
|
|
9
|
+
self.send( limit, user, type, name )
|
|
10
|
+
else
|
|
11
|
+
limit.call( user, type, name )
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require 'active_record/validations'
|
|
2
|
+
|
|
3
|
+
module Skr
|
|
4
|
+
|
|
5
|
+
class InvalidGlTransaction < ActiveRecord::RecordInvalid
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
module Concerns
|
|
10
|
+
|
|
11
|
+
module HasGlTransaction
|
|
12
|
+
|
|
13
|
+
extend ActiveSupport::Concern
|
|
14
|
+
|
|
15
|
+
module InstanceMethods
|
|
16
|
+
|
|
17
|
+
# N.B. We are overriding ActiveRecord::Base#save
|
|
18
|
+
# This is so we can wrap the save call inside a {GlTransaction.record} block
|
|
19
|
+
# which will collect any GlPostings and then collapse them down at the end of the block
|
|
20
|
+
# in order to prevent multiple postings to the same account.
|
|
21
|
+
def save(*)
|
|
22
|
+
opts = options_for_gl_transaction
|
|
23
|
+
# do nothing if the options specified an if clause and the method returns false
|
|
24
|
+
if opts.present? and opts.has_key?(:if) and self.send( opts[:if] ) == false
|
|
25
|
+
super
|
|
26
|
+
else
|
|
27
|
+
save_status = false
|
|
28
|
+
# First we wrap it up in our transaction since we don't have the benefit of
|
|
29
|
+
# save's rollback_active_record_state! block
|
|
30
|
+
# I'm tempted to appropriate it's usage, but am not since it's not documented (that I've found)
|
|
31
|
+
# and is thus likely to change
|
|
32
|
+
GlTransaction.transaction( requires_new: true ) do
|
|
33
|
+
# Enclose in GlTransaction#record block
|
|
34
|
+
gl_transaction = GlTransaction.record do
|
|
35
|
+
save_status = super # ActiveRecord::Base#save
|
|
36
|
+
# If save was successfull, pass attributes back to GlTransaction#record.
|
|
37
|
+
# Otherwise pass false back so it knows to not save the GlTransaction
|
|
38
|
+
save_status ? { attributes: self.send( opts[:attributes] ) } : false
|
|
39
|
+
end
|
|
40
|
+
# if we saved successfully, but the GlTransaction did not;
|
|
41
|
+
# Copy the errors to the model and abort the transaction
|
|
42
|
+
if save_status && gl_transaction.errors.any?
|
|
43
|
+
self.errors[:gl_transaction] += gl_transaction.errors.full_messages
|
|
44
|
+
save_status = false
|
|
45
|
+
raise InvalidGlTransaction.new(gl_transaction)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
return save_status
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
module ClassMethods
|
|
55
|
+
|
|
56
|
+
def has_gl_transaction( options={} )
|
|
57
|
+
options.merge!( attributes: :attributes_for_gl_transaction )
|
|
58
|
+
cattr_accessor :options_for_gl_transaction
|
|
59
|
+
self.options_for_gl_transaction = options unless options.empty?
|
|
60
|
+
self.send :include, InstanceMethods
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|